{"record":{"id":"52659d4ee5bcba8a","repo":"Hmbown/CodeWhale","slug":"no-screenshot-taken-yet-on-this-computer-call-screenshot-52659d","errorCode":null,"errorMessage":"no screenshot taken yet on this computer — call screenshot first","messagePattern":"no screenshot taken yet on this computer — call screenshot first","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/win32.mjs","lineNumber":428,"sourceCode":"      const file = path.resolve(outPath || path.join(dir, `shot-${crypto.randomBytes(6).toString(\"hex\")}.png`));\n      const meta = await psJson(`Add-Type -AssemblyName System.Windows.Forms; Add-Type -AssemblyName System.Drawing;\n$bounds = [System.Windows.Forms.SystemInformation]::VirtualScreen;\n${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;`}\n${region == null ? \"\" : `$crop = New-Object System.Drawing.Rectangle(${region.join(\",\")}); if (-not $bounds.Contains($crop)) { throw 'region is outside capture bounds' }; $bounds = $crop;`}\n$bmp = New-Object System.Drawing.Bitmap($bounds.Width, $bounds.Height);\ntry {\n  $g = [System.Drawing.Graphics]::FromImage($bmp);\n  try { $g.CopyFromScreen($bounds.X, $bounds.Y, 0, 0, $bounds.Size); } finally { $g.Dispose(); }\n  $bmp.Save('${file.replace(/'/g, \"''\")}', [System.Drawing.Imaging.ImageFormat]::Png);\n} finally { $bmp.Dispose(); }\n@{ x = $bounds.X; y = $bounds.Y; w = $bounds.Width; h = $bounds.Height } | ConvertTo-Json -Compress;`, { timeoutMs: 30_000 });\n      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\");\n      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() };\n      return { ...lastRaster };\n    },\n    zoom: async ({ source, region, path: outPath }) => {\n      const src = source ?? lastRaster?.file;\n      if (!src) throw new ExecError(\"no screenshot taken yet on this computer — call screenshot first\");\n      const out = outPath || path.join(recordingsDir(), `zoom-${crypto.randomBytes(4).toString(\"hex\")}.png`);\n      const script = `Add-Type -AssemblyName System.Drawing;\n$img = [System.Drawing.Image]::FromFile('${src.replace(/'/g, \"''\")}');\n$rect = New-Object System.Drawing.Rectangle(${Math.round(region[0])}, ${Math.round(region[1])}, ${Math.round(region[2])}, ${Math.round(region[3])});\n$bmp = New-Object System.Drawing.Bitmap($rect.Width, $rect.Height);\n$g = [System.Drawing.Graphics]::FromImage($bmp);\n$g.DrawImage($img, (New-Object System.Drawing.Rectangle(0, 0, $rect.Width, $rect.Height)), $rect, [System.Drawing.GraphicsUnit]::Pixel);\n$g.Dispose();\n$bmp.Save('${out.replace(/'/g, \"''\")}', [System.Drawing.Imaging.ImageFormat]::Png);\n$bmp.Dispose(); $img.Dispose();\nWrite-Output '{\"ok\": true}';`;\n      const r = await psOk(script, { timeoutMs: 20_000 });\n      if (r.code !== 0 || !fs.existsSync(out)) throw new ExecError(`zoom failed: ${(r.stderr || \"\").slice(0, 250)}`, r);\n      return { file: out, bytes: fs.statSync(out).size, region, source: src };\n    },\n    left_click: ({ target, strategy }) => { assertEventStrategy(strategy); return clickAt(0, target.x, target.y, 1); },\n    double_click: ({ target }) => clickAt(0, target.x, target.y, 2),\n    triple_click: ({ target }) => clickAt(0, target.x, target.y, 3),","sourceCodeStart":410,"sourceCodeEnd":446,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/win32.mjs#L410-L446","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Call screenshot() first, then zoom with the desired region","Pass an explicit source path to an existing PNG if you want to zoom a specific image","Handle the error by falling back to a fresh screenshot before retrying zoom"],"exampleFix":"// before\nawait zoom({ region: [0, 0, 200, 200] });\n// after\nawait screenshot();\nawait zoom({ region: [0, 0, 200, 200] });","handlingStrategy":"fallback","validationCode":"function ensureSource(backend, args = {}) {\n  if (args.source == null && !backend.lastRaster?.file) {\n    throw new Error(\"call screenshot() before zoom(), or pass an explicit source PNG path\");\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await backend.zoom({ region });\n} catch (e) {\n  if (String(e.message).includes(\"no screenshot taken yet\")) {\n    await backend.screenshot();\n    return await backend.zoom({ region });\n  }\n  throw e;\n}","preventionTips":["Always screenshot before zoom in the same backend session","Remember zoom crops an existing PNG, not the live screen","Pass an explicit source path when zooming arbitrary images","Re-screenshot after backend restarts since lastRaster is in-memory"],"tags":["screenshot","zoom","state","windows"],"backgroundTag":"invalid-state-transition","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}