heygen-com/hyperframes · critical · Error
screenshot returned no data
Error message
screenshot returned no data
What it means
Thrown by captureMarkerOnionSkin when page.screenshot({ type: 'png' }) returns a falsy buffer. Puppeteer normally returns a Uint8Array; a null/empty result indicates the headless browser failed to capture — typically a GPU process crash, a page navigation/close during capture, or a DevTools protocol error.
Source
Thrown at packages/cli/src/commands/motionShot.ts:634
)) as OnionElement[];
const windowStr = frame.hasWindow ? ` · t ${times[0]}–${times[times.length - 1]}s` : "";
const label = `${cameraLabel(camera)} · ${frame.layout === "strip" ? "filmstrip" : frame.fit ? "zoom-fit" : "1:1"} · ${times.length} frames${windowStr}`;
const svg = buildOnionSvg(elements, {
layout: frame.layout,
fit: frame.fit,
width: size.width,
height: size.height,
label,
});
await page.evaluate((markup: string) => {
document.body.insertAdjacentHTML("beforeend", markup);
}, svg);
await new Promise((r) => setTimeout(r, 60));
const buf = await page.screenshot({ type: "png" });
if (!buf) throw new Error("screenshot returned no data");
writeFileSync(outPath, buf as Uint8Array);
return outPath;
}
/** Render `projectDir`'s index headless, sample each element's motion as a 3D
* onion-skin, screenshot to `outPath` (PNG). Returns the saved path. */
export async function captureMotionPathShot(
projectDir: string,
requestsIn: ShotRequest[],
outPath: string,
opts: ShotOptions = {},
): Promise<string> {
ensureShotOutputDir(outPath);
let requests = requestsIn;
const samples = Math.max(1, Math.min(60, opts.samples ?? 9));
const layout = opts.layout ?? "path";
const fit = opts.fit ?? true;
const camera = parseAngle(opts.angle);View on GitHub (pinned to c2996c8626)
Solutions
- Retry the shot once — transient headless crashes often succeed on retry
- Pin a known-good Chrome/Chromium executable version compatible with puppeteer-core
- Reduce the composition viewport size if it is very large
- Check GPU mode resolution (resolveCaptureBrowserGpuMode) and force swiftshader or hardware as appropriate for the environment
- Ensure no code navigates or closes the page during capture
Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight: confirm the page is alive and the viewport is reasonable before capture
const alive = await page.evaluate(() => !!(document && document.body));
if (!alive) throw new Error('Page unavailable before screenshot');
if (size.width * size.height > 16_000_000) throw new Error('Viewport too large for stable screenshot'); Try / catch
async function screenshotWithRetry(page: Page, attempts = 2): Promise<Uint8Array> {
for (let i = 0; i < attempts; i++) {
const buf = await page.screenshot({ type: 'png' });
if (buf) return buf as Uint8Array;
}
throw new Error('screenshot returned no data after retries');
} Prevention
- Pin a Chrome/Chromium version known compatible with the bundled puppeteer-core
- Avoid extremely large viewports that can fail allocation in headless mode
- Ensure no navigation/close happens between SVG insertion and screenshot
When it happens
Trigger: Headless Chrome GPU process crash mid-screenshot. The page navigating or closing between the SVG insert and the screenshot call. A CDP protocol disconnection. Extreme viewport sizes that fail allocation.
Common situations: Unstable headless Chrome in CI (especially under --use-gl=swiftshader). Compositions with very large canvas sizes. Browser binaries mismatched with the puppeteer-core version. OOM conditions.
Related errors
- ghost composite returned no data
- [build-zip] chrome-headless-shell cache missing at ${baseDir
- beat analyzer not loaded
- ${err instanceof Error ? err.message : String(err)}${detail}
- Failed to load @puppeteer/browsers: ${cause} Fix: run `npm i
AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12).
Data as JSON: /api/errors/a50c027f4cad46e1.
Report an issue: GitHub.