Skip to content

fix(cli): render --json gives each missing tool, a held slot and an old Node its own code - #7842

Open
wonjangcloud9 wants to merge 2 commits into
heygen-com:mainfrom
wonjangcloud9:fix/render-json-error-codes
Open

wonjangcloud9 wants to merge 2 commits into
heygen-com:mainfrom
wonjangcloud9:fix/render-json-error-codes

Conversation

@wonjangcloud9

Copy link
Copy Markdown
Contributor

What

A failed hyperframes render --json now says which failure it was in error.code:

Cause error.code before after
Chrome not found (bad HYPERFRAMES_BROWSER_PATH, or none installed and it cannot be installed) runtime_error chrome_not_found
FFmpeg not found runtime_error ffmpeg_not_found
FFprobe not found runtime_error ffprobe_not_found
--no-wait while another render holds the slot runtime_error render_locked
Node older than 22 no JSON at all (plain text on stderr) node_version_unsupported (stderr text unchanged)

Every other failure keeps runtime_error. Messages, hints, exit codes and stderr are unchanged.

Why

#7408: a script or agent running render --json could only tell these failures apart by matching the human message text. They call for different reactions (install a tool, wait and retry, switch Node), so the code should carry the difference.

Related work

Refs #7408. This covers the first "Done when" item of that issue (cases 2–6 and 9). It deliberately leaves the other two — a JSON object on SIGINT/SIGTERM and on success — out: both change what a --json run prints on stdout today (nothing), and the issue is labeled triage/needs-design, so I did not want to pick that contract for you. Happy to follow up on those once the shape is decided.

No open PR touches this (searched by issue number and by the code names).

How

  • CliRuntimeError takes a code option, the way CliUsageError already does (default runtime_error), and failCommand(exitCode, cause, code) passes it through. errorCode() in utils/jsonError.ts reads it from either typed error.
  • The render environment check (browser/preflight.ts) sets code on the three "not found" outcomes; executeLocalRender fails with the code of the last failed check, which is the one whose title the envelope already reports as message (so code and message always describe the same check). "FFmpeg cannot start", "Chrome cannot start", missing shared libraries and low disk space keep runtime_error.
  • ensureBrowserForRender (render/execute.ts) and acquireRenderSlot (render/renderSlot.ts) pass chrome_not_found and render_locked.
  • bin/hyperframes.mjs runs before anything else is loaded, so the Node check prints its own envelope (runtimeVersionJson) when --json is on the command line, next to the unchanged stderr line.
  • docs/packages/cli.mdx: a table of these codes under the --json failure envelope.

Test plan

  • Unit tests added/updated — utils/jsonError.test.ts (a runtime error's own code, with the reported failure and with a cause), browser/preflight.test.ts (the three not-found outcomes carry their code), utils/renderLock.test.ts (acquireRenderSlot under --no-wait fails with render_locked), runtimeVersion.test.ts (envelope only for a --json run). npx vitest run over src/utils, src/browser, src/commands/render: the only 3 failures (publishProject.test.ts Unicode-form case, 2 WebGL cases in checkSweep.chromium.test.ts) fail the same way on main on this machine.
  • Manual testing performed — real CLI, macOS arm64, main (e426ce4) vs this branch, same project and same commands (render . --json --quality draft --workers 2 -o out.mp4, stdout only):
# main
[bad browser path]       exit=1 {"ok":false,"error":{"code":"runtime_error","message":"Chrome not found","hint":"HYPERFRAMES_BROWSER_PATH is set to /nope/chrome, which does not exist. Fix the path or unset it."}}
[ffprobe missing]        exit=1 {"ok":false,"error":{"code":"runtime_error","message":"FFprobe not found","hint":"Configured path does not exist: HYPERFRAMES_FFPROBE_PATH=\"/nope\""}}
[ffmpeg missing]         exit=1 {"ok":false,"error":{"code":"runtime_error","message":"FFmpeg not found","hint":"Configured path does not exist: HYPERFRAMES_FFMPEG_PATH=\"/nope\""}}
[slot held, --no-wait]   exit=1 {"ok":false,"error":{"code":"runtime_error","message":"Another render is running (pid 45795, project …/i4435-heavy, running 4s)"}}
[Node 20.20.2]           exit=1 (stdout empty)

# this branch
[bad browser path]       exit=1 {"ok":false,"error":{"code":"chrome_not_found","message":"Chrome not found","hint":"HYPERFRAMES_BROWSER_PATH is set to /nope/chrome, which does not exist. Fix the path or unset it."}}
[ffprobe missing]        exit=1 {"ok":false,"error":{"code":"ffprobe_not_found","message":"FFprobe not found","hint":"Configured path does not exist: HYPERFRAMES_FFPROBE_PATH=\"/nope\""}}
[ffmpeg missing]         exit=1 {"ok":false,"error":{"code":"ffmpeg_not_found","message":"FFmpeg not found","hint":"Configured path does not exist: HYPERFRAMES_FFMPEG_PATH=\"/nope\""}}
[slot held, --no-wait]   exit=1 {"ok":false,"error":{"code":"render_locked","message":"Another render is running (pid 59251, project …/i4435-heavy, running 4s)"}}
[Node 20.20.2]           exit=1 {"ok":false,"error":{"code":"node_version_unsupported","message":"HyperFrames requires Node.js >= 22 (current: 20.20.2). Switch Node versions and retry."}}

Unchanged on both: a successful render (exit 0, empty stdout, same 13,336-byte mp4) and an unrelated failure (render ./nope-dir --json → runtime_error, "Not a directory: …"). Not run by hand: the "no Chrome installed and offline" case — it goes through the same Chrome not found outcome as the bad-path case and is covered by the preflight unit test.

  • Documentation updated (if applicable)
  • Comments follow CONTRIBUTING.md "Comments": no comments added (scripts/comment-ratchet.mjs passes)

Also run locally: oxlint, oxfmt --check, tsc --noEmit (cli), fallow audit --base origin/main, check:cli-process-ownership, check-docs-cli-commands.mjs.

🤖 Generated with Claude Code

…ld Node its own code

Every failed `render --json` answered with `error.code: "runtime_error"`, so a
script could only tell a missing FFmpeg from a busy render slot by matching the
human message. A CliRuntimeError now carries a code, as CliUsageError does, and
the envelope reports it: chrome_not_found, ffmpeg_not_found, ffprobe_not_found,
render_locked. A Node older than 22 prints the envelope too
(node_version_unsupported) when the run asked for --json. Other failures keep
runtime_error.

Refs heygen-com#7408

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@mintlify

mintlify Bot commented Nov 15, 2026 •

Copy link
Copy Markdown

Preview approval required

This pull request comes from a fork, so Mintlify won't build a preview until someone with write access approves commit 796c051.

Approve preview

… export

bin/hyperframes.mjs now imports runtimeVersionJson, so the stub dist file
binEntry.test.ts writes must export it too, or the bin fails to load.

This branch has not been deployed

No deployments
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