Skip to content

fix(api): describe a failed snapshot build by its cause, not its tail (DEV-2570) - #239

Merged
demtario merged 3 commits into
masterfrom
fix/DEV-2570-snapshot-build-failure-message
Aug 20, 2026
Merged

fix(api): describe a failed snapshot build by its cause, not its tail (DEV-2570)#239
demtario merged 3 commits into
masterfrom
fix/DEV-2570-snapshot-build-failure-message

Conversation

@demtario

@demtario demtario commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Problem

A snapshot install/build that failed was described with execTail — the last 800/1200 characters of stderr + stdout. A tail cuts at the front, so the first line of the message was whatever the tool printed last: pnpm's progress counter (DEMOS-1W) or the middle of its self-update box (DEMOS-1Y). Sentry titles an issue from that first line, so neither issue named the failure — and because the message was multi-line, the browser SDK fed lines 2..n to its stack-frame regexes and invented the culprit │ Changelog: (v/11.22).

One failure, two mistitled issues: the Worker files one from the catch-all in index.ts, and the browser files another because describeApiFailure relays body.error verbatim into an ApiError.

Fix

DEV-2533 already solved this for the Tier-2 boot log. Rather than a second copy of those regexes, the rule set moves to packages/runtime/src/failure-log.ts: bootFailureDetail stays as a thin wrapper with the boot defaults (and the same export container-boot-failure.test.mjs imports), and a new execFailureDetail handles the builder's two streams.

Its tiers run across the streams rather than within a concatenation of them — an announced cause on either stream outranks a merely mentioned one on either — because pnpm keeps counting on stdout after diagnosing on stderr, while vite/ng/next report real errors on stdout and leave deprecation noise on stderr. The window, the empty-output wording, and the byte cap on the kept log became options; the boot path keeps its previous behaviour exactly (uncapped tail — capping here would measure the cleaned, redacted string rather than the 2500 bytes the status route already tailed).

runBuild now throws BuildFailure: a one-line message, with the output kept apart in log and the machine code in code, mirroring ContainerBootFailure. The route catch-all reports it under context: snapshot-build, fingerprinted by phase and code so one defect stays one issue, with the log as an extra.

Before / after, on the DEMOS-1W fixture:

install failed: tps://registry.npmjs.org/          <- old rule, and the message continued for 6 more lines
install failed: ERR_PNPM_NO_MATCHING_VERSION  No matching version found for handsontable@13106 …

Rollout

The message changes, so grouping changes: DEMOS-1W and DEMOS-1Y stop receiving events and correctly titled issues appear in their place. Expected, not a regression — resolve both once the successors show up.

Out of scope

The underlying handsontable@13106 install failure is DEMOS-1X / DEV-2565. The duplicate client-side report also stays: describeApiFailure files an issue for every 500, and the single-line message makes that issue readable, which is what this ticket is about; suppressing the duplicate is a separate change.

Verification

  • pnpm test — 767 tests, 765 pass, 0 fail, 2 pre-existing todo. 16 new in pipeline/failure-log.test.mjs, each verified red against the old tail rule; container-boot-failure.test.mjs passes unedited, which is the regression guard for the extraction.
  • pnpm -r run typecheck — 4 projects clean.
  • wrangler deploy --dry-run — the Worker bundles the new @handsontable/demo-runtime/failure-log subpath.
  • Not run: the end-to-end acceptance (a real install failure producing an ERR_PNPM_*-titled issue). Needs a deploy.

🤖 Generated with Claude Code


Note

Cursor Bugbot is generating a summary for commit 0d2d246. Configure here.

A snapshot install/build that failed was described with `execTail` — the last
800/1200 characters of stderr + stdout. A tail cuts at the front, so the first
line of the message was whatever the tool printed last: pnpm's progress counter
(Sentry DEMOS-1W) or the middle of its self-update box (DEMOS-1Y). Sentry titles
an issue from that first line, so neither issue named the failure, and because
the message was multi-line the browser SDK fed lines 2..n to its stack-frame
regexes and invented the culprit `│ Changelog: (v/11.22)`.

DEV-2533 already solved this for the Tier-2 boot log. Rather than a second copy
of those regexes, move the rule set to `packages/runtime/src/failure-log.ts`:
`bootFailureDetail` stays as a thin wrapper with the boot defaults (and the same
export `container-boot-failure.test.mjs` imports), and a new `execFailureDetail`
handles the builder's two streams. Its tiers run across the streams rather than
within a concatenation of them — an announced cause on either stream outranks a
merely mentioned one on either — because pnpm keeps counting on stdout after
diagnosing on stderr, while vite/ng/next report real errors on stdout and leave
deprecation noise on stderr. The window, the empty-output wording, and the byte
cap on the kept log become options.

`runBuild` now throws `BuildFailure`: a one-line message, with the output kept
apart in `log` and the machine code in `code`, mirroring `ContainerBootFailure`.
The route catch-all reports it under `context: snapshot-build`, fingerprinted by
phase and code so one defect stays one issue, with the log as an `extra`.

Rollout: the message changes, so grouping changes. DEMOS-1W and DEMOS-1Y stop
receiving events and correctly titled issues appear in their place.

DEV-2570

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0d2d246. Configure here.

Comment thread runner/packages/runtime/src/failure-log.ts Outdated
demtario and others added 2 commits August 20, 2026 11:21
… reset from erasing one

Two defects found reviewing the picker against real build output.

`vite build` announces its failure with a section label — `error during build:` —
and puts the error on the next line. The announcing tier stopped on the label, so
every vite failure described itself as `build failed: error during build:`: no
package, no import, nothing. It is the build command for nearly every framework in
the catalog, so that was the common path rather than an edge case. A cause line
that ends in a colon now takes the line under it, joined rather than replaced —
the label says which tool failed, the line under it says what did.

`\x1b[nG` at the END of a line resets nothing, since there is no next frame on that
line to keep, but `LINE_RESET` treated it as one and the slice then deleted the
line whole. A log that was one such line — `ERR_PNPM_OUTDATED_LOCKFILE …\x1b[0G` —
came out as "no output", losing the cause and, because the report site only
attaches a non-empty log, the `buildLog` extra with it. Trailing resets are dropped
before the last-frame-wins slice; a redraw mid-line still collapses as before.

The `ELIFECYCLE`-on-stdout fixture went with them: `runBuild` execs the build
binary off `node_modules/.bin` rather than an npm script, so pnpm's epilogue never
appears on that stream. It is now a real vite log, which is what surfaced the first
defect.

DEV-2570

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ict the other

The tail cap keeps the END of the join, and `execFailureDetail` joined stdout then
stderr — so a stream of deprecation warnings long enough to fill the budget on its
own dropped stdout whole. Measured at the builder's 4000-character cap: 60 lines of
`DeprecationWarning` on stderr, and the vite line the cause had just been picked
from was absent from `buildLog`, leaving an extra that explained nothing (Bugbot,
PR #239).

Each stream is now capped in its own right, and a stream that does not need its
half lends the remainder to the other so a quiet stderr does not cost stdout half
the budget for nothing.

DEV-2570

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@demtario
demtario merged commit 4875ea8 into master Aug 20, 2026
7 checks passed
@demtario
demtario deleted the fix/DEV-2570-snapshot-build-failure-message branch August 20, 2026 09:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant