garrytan/gstack · error · Error

Screenshot too large for --base64 (>10MB). Use disk path ins

Error message

Screenshot too large for --base64 (>10MB). Use disk path instead.

What it means

In `--base64` mode the screenshot is captured to an in-memory buffer and returned inline as a `data:image/png;base64,...` URI. A hard 10 MiB cap (lines 515-516) prevents the response from bricking JSON parsers, IPC channels, or LLM context windows. Only the base64 path enforces this; disk writes go through a separate path-size guard.

Source

Thrown at browse/src/meta-commands.ts:516

      // --base64 mode: capture to buffer instead of disk
      if (base64Mode) {
        let buffer: Buffer;
        if (targetSelector) {
          const resolved = await bm.resolveRef(targetSelector);
          const locator = 'locator' in resolved ? resolved.locator : page.locator(resolved.selector);
          buffer = await locator.screenshot({ timeout: 5000 });
        } else if (clipRect) {
          buffer = await page.screenshot({ clip: clipRect });
        } else {
          buffer = await page.screenshot({ fullPage: !viewportOnly });
          // Guard the most common API-bricking case (fullPage). Element /
          // clip captures usually stay within the cap; we still guard the
          // path-mode below for fullPage writes.
          ({ buffer } = await guardScreenshotBuffer(buffer));
        }
        if (buffer.length > 10 * 1024 * 1024) {
          throw new Error('Screenshot too large for --base64 (>10MB). Use disk path instead.');
        }
        return `data:image/png;base64,${buffer.toString('base64')}`;
      }

      if (targetSelector) {
        const resolved = await bm.resolveRef(targetSelector);
        const locator = 'locator' in resolved ? resolved.locator : page.locator(resolved.selector);
        await locator.screenshot({ path: outputPath, timeout: 5000 });
        return `Screenshot saved (element): ${outputPath}`;
      }

      if (clipRect) {
        await page.screenshot({ path: outputPath, clip: clipRect });
        return `Screenshot saved (clip ${clipRect.x},${clipRect.y},${clipRect.width},${clipRect.height}): ${outputPath}`;
      }

      await page.screenshot({ path: outputPath, fullPage: !viewportOnly });
      if (!viewportOnly) await guardScreenshotPath(outputPath);

View on GitHub (pinned to 94993f7401)

Solutions

  1. Drop `--base64` and pass a disk path so the PNG is written and only the path is returned: `browse screenshot out.png`.
  2. Scope the capture with `--viewport` to limit it to the visible area.
  3. Scope the capture with a selector (`--selector <css>` or `@ref`) or `--clip x,y,w,h` to shrink the buffer.

Example fix

// before
browse screenshot --base64
// after
browse screenshot --viewport out.png
Defensive patterns

Strategy: fallback

Validate before calling

// Before --base64, estimate the page height; if very tall, prefer a path.
const viewport = await bm.getPage().viewportSize();
const estimatedBytes = (viewport?.width ?? 1280) * (await bm.getPage().evaluate(() => document.documentElement.scrollHeight)) * 3;
if (estimatedBytes > 8 * 1024 * 1024) useDiskPath = true;

Try / catch

try {
  return await browse.screenshot(['--base64']);
} catch (err) {
  if (/too large for --base64/.test(err.message)) {
    // fall back to disk capture and return the path
    return await browse.screenshot(['out.png']);
  }
  throw err;
}

Prevention

When it happens

Trigger: `browse screenshot --base64` against a very tall page (fullPage default) whose PNG buffer exceeds 10*1024*1024 bytes (line 515). Most commonly a long scrollable page captured without `--viewport`.

Common situations: Agents piping screenshots into a chat/LLM context; CI capturing full-page regression shots of infinite-scroll or very long article pages.

Related errors


AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12). Data as JSON: /api/errors/46f95a2c651237b2. Report an issue: GitHub.