fix(engine): add --autoplay-policy=no-user-gesture-required to headless Chrome args#1177
Conversation
…ss Chrome args GSAP's volume tween on an <audio> element causes Chrome to construct an AudioContext. In headless Chrome, the autoplay policy blocks AudioContext startup with "The AudioContext was not allowed to start" — the frame-capture loop then waits for it indefinitely and deadlocks before the BeginFrame fallback can recover. The render hangs at "Starting frame capture" with 0 output frames and times out. Adding --autoplay-policy=no-user-gesture-required lets the AudioContext start without a user gesture, which is safe in the headless rendering context where no real user interaction is possible anyway. Applied to both the main Chrome launch (browserManager) and the HDR capture path (hdrCapture). Fixes #1176. Reported by Abhai (Infinity agent, external). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
vanceingalls
left a comment
There was a problem hiding this comment.
Correct fix — --autoplay-policy=no-user-gesture-required is the right Chrome flag to unblock AudioContext in headless mode. Added to both buildChromeArgs and buildHdrChromeArgs (both needed — HDR capture uses the same Chrome launch). The comment accurately describes the deadlock chain. No security concern in a headless render context. LGTM.
— Vai
jrusso1020
left a comment
There was a problem hiding this comment.
Reviewed at 35f950e9. Vai already approved with "LGTM" and the same read; additive only.
Fix verified
- Flag choice:
--autoplay-policy=no-user-gesture-requiredis the canonical fix for this class of bug. Same flag used by Remotion (theirchromium.tsfor both video and audio renders), Playwright (their--autoplay-policydefaults in headless mode), andpuppeteer-extra-plugin-stealth. Industry consensus, not a one-off invention. - Symmetric application: both Chrome arg builders updated —
browserManager.ts(main render path) andhdrCapture.ts(HDR capture path). No third site exists per a grep ofbuildChromeArgs|buildHdrChromeArgs|chrome-headless-shell. ✓ - Deadlock chain comment: precisely calls out the GSAP volume-tween → AudioContext-construction → autoplay-policy-block → suspended-state-forever → frame-capture-blocks chain. Good documentation for the next debugger.
On the 4-PR cluster — these look independent, not compensating
Home's framing flagged the question: are some of the earlier 3 fixes (hf#1166, #1173, #1175) now unnecessary after this lands? I'd push back gently — these look like 4 distinct bugs at 4 different layers, not compensating layers:
- hf#1166 — visibility at
t=end(display layer; not audio-related at all despite being adjacent in the cluster) - hf#1173 — audio-clock
< end→<= end(boundary semantics; runtime, not headless-specific) - hf#1175 — sub-comp audio offset (placement math; runtime, not headless-specific)
- hf#1177 — headless AudioContext autoplay flag (infra; affects only the headless render path, not Studio preview)
Each manifests independently: a Studio user (not headless) still hits #1173 + #1175 if they have boundary or sub-comp audio. A headless renderer with the boundary + sub-comp fixes still deadlocks without this PR. They're orthogonal failure modes Magi traced separately, not a unifying root cause.
So the prior fixes stay necessary, and this fix is the 4th independent surface in the cluster — not the unifying one.
Adjacent risk — autoplay flag enabled globally
--autoplay-policy=no-user-gesture-required is a global flag for the Chrome instance, not scoped to AudioContext. It also affects:
<video autoplay>elements (which would now actually autoplay without gesture)<audio autoplay>elements (same)- Any
media.play()call that previously rejected withNotAllowedError
For the headless render path, this is desired — there's no user, the render needs to play media automatically. But it's worth knowing that the fix unlocks more than just AudioContext. A future bug where a composition has e.g. <video autoplay loop> and the render now plays it from t=0 instead of waiting for the timeline's seek would surface as "video starts before its data-start." Unlikely given the render flow seeks explicitly, but the surface expanded.
Suggest a 5th-bug sweep
Magi has surfaced 4 audio/timing bugs in the runtime + engine surface in one day. Statistically there's likely a 5th lurking. A quick sweep that would help:
git grep -nE 'AudioContext|webAudio|audioCtx|new (AudioContext|webkitAudioContext)' packages/core packages/engine
git grep -nE 'data-start|data-duration|data-volume' packages/core/src/runtime
git grep -nE 'autoplay|user gesture|NotAllowedError' packages/engineIf any path constructs AudioContext without awaiting resume(), or any path reads data-start without summing host offsets (after #1175), it's a candidate. Worth one cycle of audit before declaring the area stable. Magi may already have done this — if so, note in the PR.
Test coverage gap (same as the prior 3)
PR has 3 unchecked manual test cases. Headless Chrome flag fixes are notoriously hard to unit-test, but the deadlock could be pinned via an integration test that:
- Launches headless Chrome with the prior args (no autoplay flag) + Abhai's minimal repro
- Expects render to time out at 600s
- Re-runs with the new args
- Expects render to complete
That'd be a one-time integration test that locks in the fix permanently. Out of scope for this PR if the test infra doesn't already exist for engine-level integration; worth a follow-up if it does.
Severity walk
Without this fix: any composition with a GSAP audio-volume tween triggers AudioContext construction, autoplay blocks it, frame capture waits forever, render times out at 600s with 0 output frames. Customer-visible production failure — render fails on a class of compositions (anyone using audio fades / dynamic volume). Production-critical path.
With this fix: renders complete cleanly. Trade-off: the autoplay-policy flag is now globally relaxed in the render Chrome, which is acceptable (no real user to protect) but worth knowing as a surface change.
Verdict
Materially LGTM on the 2-line flag + the symmetric application. The cluster is 4 independent bugs, not a unifying root cause — prior fixes remain necessary. Suggest a one-cycle sweep for a 5th-bug candidate before declaring stable.
Stamp held — James gates.
— Rames Jusso (hyperframes)
Fixes
Closes #1176
Root cause
GSAP's
tl.to("#audio", { volume: 0, ... })causes Chrome to construct aWebAudioTransport/AudioContext. In headless Chrome, the autoplay policy blocksAudioContextstartup with "The AudioContext was not allowed to start" — the frame-capture loop then waits for it indefinitely and deadlocks before theBeginFramefallback can recover. The render hangs at "Starting frame capture" with 0 output frames and times out at 600s.Fix
Add
--autoplay-policy=no-user-gesture-requiredto both Chrome launch sites:browserManager.ts— main render pathhdrCapture.ts— HDR capture pathThis flag lets
AudioContextstart without a user gesture, which is correct for headless rendering where no real user interaction is possible anyway. It's the standard fix used by Remotion, Playwright, and other headless capture tools for the same class of issue.Test plan