Skip to content

fix(producer): a failure before the first frame reports frame capture as its stage - #7331

Merged
miguel-heygen merged 2 commits into
mainfrom
dpins/failed-stage-names-capture
Nov 6, 2026
Merged

miguel-heygen merged 2 commits into
mainfrom
dpins/failed-stage-names-capture

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

What

A render that fails before its first frame now reports the right failed stage. Those failures were filed as "Checking browser GPU", "Measuring capture speed" or "Checking effects, 240 of 240 frames", even when the failure came later.

Why

The failed stage is whatever label the render last set. Nothing set a new label after the GPU check, calibration or the effect scan until the first frame was captured, so every failure in that gap took the earlier label. It came up on a real render that stopped on a missing image: the failure summary read failedStage: "Checking browser GPU", and failed_stage_code in telemetry reads the same value, so failures in that gap were counted against the GPU check.

Related work

Found while testing #7304. It stands on its own.

How

The label goes back to "Starting frame capture" after each of those steps. That label and its code already exist, and a failure inside a step still names that step. People see that label while browsers start, which is what is happening.

The effect scan can stop early, so scanCssEffectLiveFrames now calls an onEnd on every exit, and CaptureOptions passes it on as onEffectScanEnd. The reset only applies while the label is still the scan's, so a scan in one worker can't overwrite another worker's "Capturing frame".

normalizeStageCode also strips "N of M", which made failed_stage_code a new value for each frame count (checking_effects_240_of_240_frames).

I checked every updateJobStatus call in renderOrchestrator.ts: these three are the steps before frame one that nothing else relabels. "Compiling composition" stays set through the probe that follows it, which is left as it is.

Test plan

  • Unit tests added: renderOrchestrator.pageNeverReady.test.ts, "names frame capture as the failed stage for a failure before frame one", after calibration and with no calibration, plus the same after the effect scan. 6 passed, 3 runs in a row. riskFrames.test.ts, "says the scan ended when it finishes, stops early over budget, or the page fails": 19 passed, 3 runs in a row. render.test.ts, normalizeStageCode: 5 passed.
  • fails without the fix: with renderOrchestrator.ts from main, both fail: expected 'Measuring capture speed' to be 'Starting frame capture' and expected 'Checking browser GPU' to be 'Starting frame capture'. With the reset on scan end removed: expected 'Checking effects, 240 of 240 frames' to be 'Starting frame capture'. With onEnd removed, the riskFrames.test.ts test fails: expected +0 to be 1.
  • Manual testing: one real failing render on a Linux box, in screenshot capture, stopped on a missing image (this fix on top of fix(engine): a render no longer finishes without an image that loaded late #7304's branch, because that is the failure it came from). Exit 1 in 92 s, and the failure summary reads "failedStage":"Starting frame capture", where it read "Checking browser GPU" before.
  • Not run: the whole producer suite (CI runs it), a Mac, Windows, a real render that fails after an effect scan (that route needs drawElement capture, which the Linux box doesn't use), and the normalizeStageCode test with its regex change removed. Past telemetry keeps its old labels, so counts of the GPU-check stage drop from this release on.
  • Comments follow CONTRIBUTING.md "Comments".

Size

About 90 added lines across 8 files, most of them tests. It stands alone because it is one kind of wrong label with nothing else to ship with.

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: REQUEST CHANGES · 1 blocker · 0 important · 0 nits · head c73eb989

  • blocker (Rule 2: the same sticky-label gap is still open on another path). renderOrchestrator.ts:3809-3816: onEffectScanProgress sets Checking effects, N of M frames while the main session is still initializing, before frame one. That happens in initDrawElementOrTransparentBackground, through cssEffectRiskFrames at engine/src/services/frameCapture.ts:1193. Nothing resets the label when the scan ends.

    Failure path: a macOS drawElement worker-encode render with a filter:blur layer (so deFrameFallback is on). The scan finishes, then a later init step stalls before frame one: armStaticDedup, or the deferred self-verify (the #7329 hang). The render then reports failedStage: "Checking effects, 240 of 240 frames". Its failed_stage_code is checking_effects_240_of_240_frames, because STAGE_COUNTS strips (…) and n/m but not "N of M". That code changes with every frame count.

    The scan can stop early (riskFrames.ts:110,120,123), so resetting the label when scanned === total isn't enough. Have the engine signal when the scan ends (for example a callback from cssEffectRiskFrames' finally), call startingFrameCapture() there, and add a case to the test.

Scope

I read both changed files in full, plus updateJobStatus and normalizeStageCode. All three labels use progress 25 and code start_browsers, so progress doesn't move backwards. A failure inside the GPU check or inside calibration still names that step: the reset comes after the await, and after the finally it sits next to. I re-ran the PR's fails-without-fix check locally: with main's renderOrchestrator.ts, both new cases fail with the messages quoted in the PR body. At head, 5 tests pass.

@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Fixed at 8b0d18d. The scan now signals its end on every exit, including the early stops and a failing page: scanCssEffectLiveFrames calls onEnd in a finally, and that resets the label through the same startingFrameCapture(). A worker's scan can't overwrite another worker's "Capturing frame", because the reset only applies while the label is still the scan's. normalizeStageCode also strips "N of M", so the code no longer changes with the frame count.

Tests: a new case after the effect scan, which fails with expected 'Checking effects, 240 of 240 frames' to be 'Starting frame capture' when the reset is removed, and an engine test for all three exits, which fails with onEnd removed.

Not run: a real render that fails after an effect scan, because that route needs drawElement capture and my Linux box doesn't use it.

@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Fallow audit report

Quality gate failed

Found 1 finding.

Health (1)
Severity Rule Location Description
major fallow/high-crap-score packages/engine/src/services/riskFrames.ts:106 'scanLiveFrames' has CRAP score 31.6 (threshold: 30.0, cyclomatic 10)

Generated by fallow.

Gate outcomes: failed audit-verdict.

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: APPROVE · 0 blocker · 0 important · 0 nits — 1 resolved, 0 open, 0 new · c73eb989 → 8b0d18d4

✅ The effect-scan label is now reset on every way the scan can end (try/finally in scanCssEffectLiveFrames), and the guard means a worker no longer overwrites a label that isn't its scan's. I confirmed it locally: removing the reset makes the new orchestrator case fail ('Checking effects, 240 of 240 frames'). Removing the \d+ of \d+ regex change makes the normalizeStageCode case fail too; that's the check the PR body lists as not run. At head: orchestrator 6 tests, riskFrames 19, normalizeStageCode 5, all passing. CI was still running when I reviewed.

@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 267 (base branch 267), smooth 124 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (8)

Unstable (1)

  • seqplainkeys-none-px-r0-root-z100: tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true

@miguel-heygen
miguel-heygen added this pull request to the merge queue Nov 6, 2026
Merged via the queue into main with commit fe0f54b Nov 6, 2026
155 of 157 checks passed
@miguel-heygen
miguel-heygen deleted the dpins/failed-stage-names-capture branch November 6, 2026 20:34
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 7, 2026
…-com#7336-heygen-com#7358

Adopt heygen-com#7304 (without CLI renderDiagnostic, needs heygen-com#6725), heygen-com#7331, heygen-com#7332, heygen-com#7334,
heygen-com#7337, heygen-com#7336, heygen-com#7339, heygen-com#7289, heygen-com#5594, heygen-com#6900, heygen-com#7322, heygen-com#7342, heygen-com#7343, heygen-com#7350 (onProgress
only), heygen-com#7352, heygen-com#7348, heygen-com#7344, heygen-com#7353, heygen-com#7356 and heygen-com#7355. Defer heygen-com#5708 (needs heygen-com#6911) and
heygen-com#7345 (READ_VERSION 4 vs 7); heygen-com#7307 not applicable. Re-read 14 moved pins, pin 6
new PRs (monitored 126). Make checkReportFile module-private and suppress the
cross-package closeTestChrome duplicate for fallow.

Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
Co-authored-by: SophiaHuangCc <129631224+SophiaHuangCc@users.noreply.github.com>
Co-authored-by: Dante <duanjl.china@gmail.com>
Co-authored-by: Muzzy5150 <muzzy5150yt@gmail.com>
Co-authored-by: ump45nose <52391318+ump45nose@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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