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

  1. Retry the shot once — transient headless crashes often succeed on retry
  2. Pin a known-good Chrome/Chromium executable version compatible with puppeteer-core
  3. Reduce the composition viewport size if it is very large
  4. Check GPU mode resolution (resolveCaptureBrowserGpuMode) and force swiftshader or hardware as appropriate for the environment
  5. 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

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


AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12). Data as JSON: /api/errors/a50c027f4cad46e1. Report an issue: GitHub.