Skip to content

feat(cli): summarize the deploy trace when a deploy fails - #294

Merged
Brooooooklyn merged 4 commits into
mainfrom
fix/deploy-log-summary
Aug 21, 2026
Merged

Brooooooklyn merged 4 commits into
mainfrom
fix/deploy-log-summary

Conversation

@Brooooooklyn

@Brooooooklyn Brooooooklyn commented Aug 21, 2026 •

Copy link
Copy Markdown
Member

The problem

Every void deploy writes a JSONL trace to ~/.void/logs/. On failure the CLI printed its path:

■  deploy: Deploy failed: deploy in progress
│  Deployment: dpl_7zgitxdrxxz9
│  Detailed log: /home/runner/.void/logs/deploy-2026-08-21T03-24-19-764Z.jsonl

That runner is destroyed with the job, so the file is unreachable exactly where the trace matters most. Seen in vite-plus#2511.

The change

runDeploy closes the logger, reads the file back, and appends a summary to the error message.

■  deploy: Deploy failed: deploy in progress
│  Deployment: dpl_7zgitxdrxxz9
│  Detailed log: /home/runner/.void/logs/deploy-2026-08-21T03-24-19-764Z.jsonl
│
│  Errors
│     9.0s  deploy_server_error
│           deploymentId=dpl_7zgitxdrxxz9
│           message=deploy in progress
│           err=PlatformApiError: Deploy failed (code=409) -> Error: another deployment...
│
│  Last 20 of 26 entries
│     8.4s  info   finalize_start        assets=98 workers=0
│     8.4s  info   deploy_post           url=https://api.void.app/projects/prj_9k1/deploy
│     8.6s  info   stream_deployment_id  deploymentId=dpl_7zgitxdrxxz9
│     9.0s  error  deploy_server_error   message=deploy in progress
│     9.0s  error  deploy_end            ok=false err=Error: deploy: Deploy failed...

Two blocks: every error record with its flattened cause chain, then the last 20 records as a timeline. It prints on every failure, in CI and locally.

Redaction

Void masks the credentials it emits itself, because the summary reaches CI logs that the trace file never did.

Pattern Catches
Signed query parameters ?X-Amz-Signature=…, &token=…
Bearer tokens Authorization: Bearer …
Field key names a credential apiToken, sessionCookie, privateKey

Masking your own values is left to your CI platform, which holds the secrets and masks them before the log is written. GitHub Actions does this for everything under secrets.*.

Void does not guess at credential-shaped variable names, and it does not parse credentials out of values you supplied. A password inside a DATABASE_URL in your build command prints as written. The docs say so, and say to register such values as CI secrets instead. auth is likewise absent from the key list — it matches metadata keys such as authMethod far more often than credentials.

Safety

A postmortem aid must never replace the failure it describes.

  • formatErrorNodeInline coerces every part of a parsed record instead of trusting its declared shape. A record with err.message: 123 used to throw TypeError: t.replace is not a function.
  • The enrichment is wrapped in try/catch. message is read-only on a frozen Error and getter-only on a DOMException, which an aborted fetch produces, so the assignment alone can throw. Losing the summary is fine; losing the deploy error is not.

Not covered

Pre-flight failures print no summary. There are 73 exit() sites reachable inside runDeploy — a missing project, a rejected flag combination, an unsupported --backend cloudflare feature. Each one calls log.error() with a full, hand-written message first, and each fires before any deploy work happens, so there is no trace to summarize. deps.exit is an injected seam the tests rely on.

A build failure prints no summary either: execSync runs with stdio: 'inherit', so the compiler output is already on the terminal in full.

Routing those sites through one finalization boundary would change exit codes and output across the Cloudflare backend, auth, and validation. That belongs in its own PR.

Files

File Change
packages/void/src/cli/log-summary.ts New. Pure formatter plus a file reader that returns null on any failure.
packages/void/src/cli/log-file.ts Export ErrChainNode; add formatErrorNodeInline for already-serialized nodes.
packages/void/src/cli/deploy.ts Close, read, append, rethrow in runDeploy's catch.
packages/void/test/unit/deploy-log-summary.test.ts New. 20 tests.
docs/reference/cli.md Document the summary.

Verification

  • vp run check — clean
  • vp lint — clean
  • vp fmt — no changes
  • vp test run packages/void — 3787 passed, 14 skipped

One browser file, packages/react/test/integration/react-pages-client.test.ts, timed out on hydration in a full local run (31 of 93 tests, page.waitForSelector('#app[data-hydrated="true"]')). deploy.ts already imported node:fs and picocolors, and log-summary.ts has no top-level side effects, so this diff adds nothing executable to the graph that test loads.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YZ88Y4fzBDSBBserEfvBrB


Note

Medium Risk
The summary is appended to stderr/CI logs, so incomplete redaction could leak credentials. Failures in summarization are swallowed so they cannot replace the original deploy error.

Overview
On a failed void deploy, the CLI now flushes the JSONL trace, reads it back, and appends a summary to the error message so the cause is visible in CI after the runner (and ~/.void/logs/) is gone.

The summary has two blocks: up to five error records with flattened cause chains (skipping the terminal deploy_end echo), then the last 20 records as a timeline. Pre-flight and build failures still print no summary.

Credentials the CLI emits itself are masked: signed query params, bearer tokens, and fields whose keys look like secrets. User-supplied values (e.g. a password inside DATABASE_URL in a build command) are left as-is for the CI secret masker. Summarization is wrapped so a read-only Error.message or a corrupt log cannot hide the original failure.

Reviewed by Cursor Bugbot for commit 38b9eab. Bugbot is set up for automated code reviews on this repo. Configure here.

Every deploy writes a JSONL trace to `~/.void/logs/`, and a failure printed
its path. On a CI runner that path dies with the job, so the cause was
unreachable exactly where it mattered most:

    deploy: Deploy failed: deploy in progress
    Deployment: dpl_7zgitxdrxxz9
    Detailed log: /home/runner/.void/logs/deploy-...jsonl   <- gone

`runDeploy` now closes the logger, reads the file back, and appends a summary
to the error message: every `error` record with its flattened cause chain,
then the last 20 records as a timeline.

The trace was a file the user opened by hand; it is now published to whatever
reads stderr. Three value patterns (signed query parameters, bearer tokens,
inline `NAME=value` credential assignments) plus whole-field masking by key
name scrub it on the way out. A build command comes from `void.json`, so
`SENTRY_AUTH_TOKEN=... npm run build` reaches the log as an ordinary field.

The summary is a postmortem aid and must never replace the failure it
describes. `formatErrorNodeInline` coerces every part of a parsed record
rather than trusting its declared shape, and the enrichment is wrapped:
`message` is read-only on a frozen `Error` and getter-only on a
`DOMException`, so the assignment alone can throw.

Not covered: paths that call `process.exit` directly, such as a Class A build
failure. Those bypass the catch, and `execSync` already streams the build
error with `stdio: 'inherit'`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ88Y4fzBDSBBserEfvBrB
@cursor

cursor Bot commented Aug 21, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_cc7d2c84-2ab7-431d-8e98-a617bc8255b9)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6165b8effb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/void/src/cli/log-summary.ts Outdated
Comment thread packages/void/src/cli/deploy.ts
The name-based pattern misses the case where the credential rides in a URI:
`DATABASE_URL=postgres://user:pw@host/db` names no credential term, and a bare
`--registry https://u:pw@host` is not an assignment at all. Both reach the
summary through `build_start`, and from there into a CI log.

Userinfo is now masked wherever a URI appears. The scheme and host survive,
because those are the parts that make the record worth reading. A userinfo with
no colon is one opaque value, which in that position is usually a token, so it
is masked whole.

Also correct the docs. They said a failed deploy prints a summary, without
qualification. Pre-flight gates and build failures exit before the catch and
print no summary — each already prints its own message, and a build streams its
output straight to the terminal.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ88Y4fzBDSBBserEfvBrB
@cursor

cursor Bot commented Aug 21, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_10c58172-4271-448c-9688-4b649fbdc404)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4786d26736

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/void/src/cli/log-summary.ts Outdated
Drop the pattern that masked `NAME=value` assignments whose name contained
TOKEN, SECRET, AUTH, and similar terms.

Name matching buys little and misses more. GitHub Actions masks every value
registered under `secrets.*` before it reaches the log, and a credential
written as a literal in `void.json` is committed to the repository already, so
the summary is not where it first becomes visible.

The masking that stays is the masking a secret masker cannot do: a password
spliced into a connection URI is not the whole secret value, so no pipeline
keyed on that value recognizes it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ88Y4fzBDSBBserEfvBrB
@cursor

cursor Bot commented Aug 21, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_81f2a3f5-416b-4444-8c0b-30cdaf7344f2)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2c49b36be1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/void/src/cli/log-summary.ts Outdated
Drop the connection-URI userinfo pattern. The summary no longer parses
credentials out of values the user supplied.

This draws the line at ownership. Void masks the credentials it emits itself —
a presigned URL it built, an auth header it set, a field it named. Everything
else belongs to the CI platform, which holds the secrets and masks them before
the log is written; GitHub Actions does this for everything under `secrets.*`.

The consequence is documented: a password inside a `DATABASE_URL` in a build
command now prints as written. The docs say so, and say to register such values
as CI secrets instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ88Y4fzBDSBBserEfvBrB
@cursor

cursor Bot commented Aug 21, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_4d582a06-0659-4421-a362-c9d939110acf)

@Brooooooklyn
Brooooooklyn merged commit aa7dd5e into main Aug 21, 2026
9 checks passed
@Brooooooklyn
Brooooooklyn deleted the fix/deploy-log-summary branch August 21, 2026 06:54
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