Hmbown/CodeWhale · error · ExecError

no screenshot taken yet on this computer — call screenshot…

Error message

no screenshot taken yet on this computer — call screenshot first

What it means

zoom crops/enlarges a region of a previously captured screenshot. The backend caches the last raster (lastRaster) per backend instance; if no screenshot has been taken yet and no explicit source path is given, it throws this ExecError telling the caller to call screenshot first.

Solutions

  1. Call screenshot() first, then zoom with the desired region
  2. Pass an explicit source path to an existing PNG if you want to zoom a specific image
  3. Handle the error by falling back to a fresh screenshot before retrying zoom

Example fix

// before
await zoom({ region: [0, 0, 200, 200] });
// after
await screenshot();
await zoom({ region: [0, 0, 200, 200] });
Defensive patterns

Strategy: fallback

Validate before calling

function ensureSource(backend, args = {}) {
  if (args.source == null && !backend.lastRaster?.file) {
    throw new Error("call screenshot() before zoom(), or pass an explicit source PNG path");
  }
}

Try / catch

try {
  return await backend.zoom({ region });
} catch (e) {
  if (String(e.message).includes("no screenshot taken yet")) {
    await backend.screenshot();
    return await backend.zoom({ region });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling zoom({region:[...]}) as the first action on a fresh backend instance; after a backend restart that cleared lastRaster; passing source: undefined/null explicitly; source pointing at logic path but never actually providing a path.

Common situations: Agents issuing zoom immediately after session start; assuming zoom reads the live screen (it only reads an existing PNG); previous screenshot call threw, so nothing was cached.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/52659d4ee5bcba8a. Report an issue: GitHub.

Appendix: source

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

      const file = path.resolve(outPath || path.join(dir, `shot-${crypto.randomBytes(6).toString("hex")}.png`));
      const meta = await psJson(`Add-Type -AssemblyName System.Windows.Forms; Add-Type -AssemblyName System.Drawing;
$bounds = [System.Windows.Forms.SystemInformation]::VirtualScreen;
${display == null ? "" : `$screens = [System.Windows.Forms.Screen]::AllScreens; if (${display} -gt $screens.Count) { throw 'display index is out of range' }; $bounds = $screens[${display - 1}].Bounds;`}
${region == null ? "" : `$crop = New-Object System.Drawing.Rectangle(${region.join(",")}); if (-not $bounds.Contains($crop)) { throw 'region is outside capture bounds' }; $bounds = $crop;`}
$bmp = New-Object System.Drawing.Bitmap($bounds.Width, $bounds.Height);
try {
  $g = [System.Drawing.Graphics]::FromImage($bmp);
  try { $g.CopyFromScreen($bounds.X, $bounds.Y, 0, 0, $bounds.Size); } finally { $g.Dispose(); }
  $bmp.Save('${file.replace(/'/g, "''")}', [System.Drawing.Imaging.ImageFormat]::Png);
} finally { $bmp.Dispose(); }
@{ x = $bounds.X; y = $bounds.Y; w = $bounds.Width; h = $bounds.Height } | ConvertTo-Json -Compress;`, { timeoutMs: 30_000 });
      if (!fs.existsSync(file) || ![meta.x, meta.y, meta.w, meta.h].every(Number.isFinite) || meta.w <= 0 || meta.h <= 0) throw new ExecError("screenshot did not return a valid raster");
      lastRaster = { file, bytes: fs.statSync(file).size, points: { x: meta.x, y: meta.y, 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}';`;
      const r = await psOk(script, { timeoutMs: 20_000 });
      if (r.code !== 0 || !fs.existsSync(out)) throw new ExecError(`zoom failed: ${(r.stderr || "").slice(0, 250)}`, r);
      return { file: out, bytes: fs.statSync(out).size, region, source: src };
    },
    left_click: ({ target, strategy }) => { assertEventStrategy(strategy); return clickAt(0, target.x, target.y, 1); },
    double_click: ({ target }) => clickAt(0, target.x, target.y, 2),
    triple_click: ({ target }) => clickAt(0, target.x, target.y, 3),

View on GitHub (pinned to 73e0f67d83)