Hmbown/CodeWhale · error · ExecError

screenshot failed

Error message

screenshot failed: ${(r.stderr || r.stdout).trim().slice(0, 300)}

What it means

The win32 screenshot action runs a System.Drawing CopyFromScreen script via ps() with a 30s timeout, then requires both a zero exit code and that the expected PNG file exists on disk. Either condition failing throws this ExecError with up to 300 chars of stderr/stdout explaining the capture failure.

Solutions

  1. Read the stderr/stdout excerpt in the message for the actual capture error
  2. Ensure the process runs in an interactive desktop session (not a service/SSH headless session)
  3. Verify System.Drawing/GDI+ works on the host and recordingsDir() is writable with free disk space
  4. Retry once — transient session/disconnect states (RDP detach) break CopyFromScreen
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const shot = await computer.screenshot();
} catch (e) {
  if (/screenshot failed/.test(e.message)) {
    // check session has an interactive desktop; retry once after a short delay
  }
}

Prevention

When it happens

Trigger: screenshot() where the PowerShell capture script exits nonzero (Add-Type/GDI failure, session without a desktop) or exits 0 but the output PNG file was never written to recordingsDir().

Common situations: Running in a session without an interactive desktop (Session 0 service, SSH without console); GDI+ / System.Drawing broken on the host; antivirus blocking the PNG write; disk full so bmp.Save fails; multi-monitor coordinate issues producing a zero-size capture.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/6020529daf2a6bcd. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/plugins/computer-use/src/backends/win32.mjs:348

    },
    screenshot: async (args = {}) => {
      if (Object.hasOwn(args, "app_ref") || Object.hasOwn(args, "window_id")) throw unsupportedSelector("Windows screenshot does not support app_ref or window_id; omit them for a desktop screenshot");
      const { display, region, path: outPath } = args;
      const dir = recordingsDir();
      fs.mkdirSync(dir, { recursive: true });
      const file = outPath || path.join(dir, `shot-${new Date().toISOString().replace(/[:.]/g, "-")}-${crypto.randomBytes(3).toString("hex")}.png`);
      const winPath = file.replace(/\\/g, "\\\\");
      const script = `Add-Type -AssemblyName System.Windows.Forms; Add-Type -AssemblyName System.Drawing;
$bounds = [System.Windows.Forms.SystemInformation]::VirtualScreen;
$bmp = New-Object System.Drawing.Bitmap($bounds.Width, $bounds.Height);
$g = [System.Drawing.Graphics]::FromImage($bmp);
$g.CopyFromScreen($bounds.X, $bounds.Y, 0, 0, $bounds.Size);
$g.Dispose();
$bmp.Save('${file.replace(/'/g, "''")}', [System.Drawing.Imaging.ImageFormat]::Png);
$bmp.Dispose();
Write-Output '{"ok": true, "w": ' + $bounds.Width + ', "h": ' + $bounds.Height + '}';`;
      const r = await ps(script, { timeoutMs: 30_000 });
      if (r.code !== 0 || !fs.existsSync(file)) throw new ExecError(`screenshot failed: ${(r.stderr || r.stdout).trim().slice(0, 300)}`, r);
      const meta = tryJson(r.stdout.trim().split("\n").pop(), {});
      lastRaster = { file, bytes: fs.statSync(file).size, points: { x: 0, y: 0, w: meta.w, h: meta.h }, pixels: { w: meta.w, h: meta.h }, scale: 1, capturedAt: new Date().toISOString() };
      return { ...lastRaster };
    },
    zoom: async ({ source, region, path: outPath }) => {
      const src = source ?? lastRaster?.file;
      if (!src) throw new ExecError("no screenshot taken yet on this computer — call screenshot first");
      const out = outPath || path.join(recordingsDir(), `zoom-${crypto.randomBytes(4).toString("hex")}.png`);
      const script = `Add-Type -AssemblyName System.Drawing;
$img = [System.Drawing.Image]::FromFile('${src.replace(/'/g, "''")}');
$rect = New-Object System.Drawing.Rectangle(${Math.round(region[0])}, ${Math.round(region[1])}, ${Math.round(region[2])}, ${Math.round(region[3])});
$bmp = New-Object System.Drawing.Bitmap($rect.Width, $rect.Height);
$g = [System.Drawing.Graphics]::FromImage($bmp);
$g.DrawImage($img, (New-Object System.Drawing.Rectangle(0, 0, $rect.Width, $rect.Height)), $rect, [System.Drawing.GraphicsUnit]::Pixel);
$g.Dispose();
$bmp.Save('${out.replace(/'/g, "''")}', [System.Drawing.Imaging.ImageFormat]::Png);
$bmp.Dispose(); $img.Dispose();
Write-Output '{"ok": true}';`;

View on GitHub (pinned to 433685b202)