Symptom: npx hyperframes render (0.7.107, macOS arm64) runs for ~19 minutes and then hard-fails with
[Parallel] Capture failed: Worker 0: Page.captureScreenshot timed out. Increase the 'protocolTimeout' setting in launch/connect calls for a higher timeout if needed.No output file. The message names puppeteer's protocolTimeout (engine default 300000ms, PRODUCER_PUPPETEER_PROTOCOL_TIMEOUT_MS), which is the symptom, not the cause: one capture worker was starved long enough that a single CDP screenshot never answered in 5 minutes.
What it actually was, on a 10-core / 24 GB M5 rendering a 47s 1080x1920@60 (2823 frames) composition with a 180 MB local <video> plus 8 backdrop-filter: blur(16px) cards:
- Swap was exhausted (9.2 GB of 10 GB used, 7.1M pageouts) before the render started.
- Auto worker sizing is
cores / coresPerWorker(2.5 default) = 4 Chrome instances, each decoding the same 180 MB video. Sizing reads cores and total RAM, never free memory or swap pressure, so a thrashing host gets the same 4 workers as an idle one. - An orphaned headless Chrome from an earlier render (
ppid 1,--headless=new, days old, ~370 MB across 4 renderer helpers) was still resident. Renderer helpers survivepkillof the parent; kill them by PID.
Fix that worked, same composition, same machine, no composition change: kill the orphans, then
PRODUCER_BROWSER_GPU_MODE=hardware PRODUCER_PUPPETEER_PROTOCOL_TIMEOUT_MS=900000 \
npx hyperframes render public -o output.mp4 --workers 318m48s failure -> 2m19s success (2823/2823 frames, artifact validated). The dominant term was concurrency, not the timeout; raising protocolTimeout alone would have bought a longer stall.
Diagnostic order for any 'render was slow then timed out' on macOS:
sysctl vm.swapusagefirst. Near-full swap explains a stalled worker better than any composition property.ps -Ao pid,ppid,command | grep 'headless=new'for orphaned render browsers;ppid=1means a previous run leaked them.- Then cap
--workers(orPRODUCER_MAX_WORKERS) to 2-3 rather than reaching for timeouts. - Keep
PRODUCER_BROWSER_GPU_MODE=hardwareon macOS; software rasterization is the other well-known way to time this out.
Cheap pre-flight that also validates the edit: npx hyperframes snapshot <dir> --at t1,t2,t3 renders single frames in ~10s and warms the media proxies, so a broken composition or a wedged browser surfaces before you spend 20 minutes on a capture run.