{"record":{"id":"ae1b7bbc374bffee","repo":"Hmbown/CodeWhale","slug":"no-screenshot-taken-yet-on-this-computer-call-screenshot","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/darwin.mjs","lineNumber":572,"sourceCode":"    state.lastRaster = {\n      file,\n      ...(window ? { app_ref, window_index: window_id ?? 0 } : {}),\n      bytes: stat.size,\n      display: disp ?? 1,\n      // Region and window rasters describe that rect, not the whole display.\n      // The PNG header is the pixel ground truth; scale is derived from\n      // pixels/points below so Retina and mixed-DPI stay exact.\n      points: window?.points ?? (region ? { x: region[0], y: region[1], w: region[2], h: region[3] } : d?.points ?? null),\n      pixels: imagePixels(file),\n      scale: d?.scale ?? 1,\n      capturedAt: new Date().toISOString(),\n    };\n    if (state.lastRaster.points?.w) state.lastRaster.scale = state.lastRaster.pixels.w / state.lastRaster.points.w;\n    return { ...state.lastRaster, path: file };\n  }\n\n  async function zoom({ source, region, path: outPath }) {\n    if (!source && !state.lastRaster) throw new ExecError(\"no screenshot taken yet on this computer — call screenshot first\");\n    const [x, y, w, h] = region;\n    if (![x, y, w, h].every((n) => Number.isInteger(n) && n >= 0) || !w || !h || x + w > state.lastRaster.pixels.w || y + h > state.lastRaster.pixels.h) throw new ExecError(\"region must be [x, y, w, h] in last-raster pixels\");\n    const src = source ?? state.lastRaster.file;\n    const dir = recordingsDir();\n    fs.mkdirSync(dir, { recursive: true });\n    const out = outPath || path.join(dir, `zoom-${crypto.randomBytes(4).toString(\"hex\")}.png`);\n    await runOk(\"sips\", [\"-s\", \"format\", \"png\", \"-c\", String(Math.round(h)), String(Math.round(w)), \"--cropOffset\", String(Math.round(y)), String(Math.round(x)), src, \"--out\", out], { timeoutMs: 15_000 });\n    const parent = state.lastRaster;\n    state.lastRaster = { file: out, bytes: fs.statSync(out).size, source: src, region,\n      points: { x: (parent.points?.x ?? 0) + x / parent.scale, y: (parent.points?.y ?? 0) + y / parent.scale, w: w / parent.scale, h: h / parent.scale },\n      pixels: { w, h }, scale: parent.scale, capturedAt: new Date().toISOString() };\n    return state.lastRaster;\n  }\n\n  // ---------- recording ----------\n  const rec = new Map(); // Includes starting children so session close owns them too.\n\n  function requestRecordingStop(r) {","sourceCodeStart":554,"sourceCodeEnd":590,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/darwin.mjs#L554-L590","documentation":"The zoom action crops from the most recent screenshot raster cached in state.lastRaster. If no screenshot has been taken in this session (or no explicit `source` is given), there is no raster to crop and the library throws. A `source` path bypasses this requirement.","triggerScenarios":"Calling zoom({ region }) without first calling screenshot() in the same backend session; after a backend restart/state reset that cleared lastRaster; calling zoom on a fresh connection while the screenshot was taken in a different session.","commonSituations":"An agent starts a new session and immediately calls zoom assuming previous state; the computer-use backend process was restarted between the screenshot and the zoom call.","solutions":["Call screenshot() before zoom() in the same session.","Pass an explicit `source` image path to zoom({ source, region }) instead of relying on the cached raster.","Re-establish state if the backend was restarted, then screenshot first."],"exampleFix":"// before\nawait zoom({ region: [0, 0, 100, 100] }); // fresh session\n// after\nawait screenshot();\nawait zoom({ region: [0, 0, 100, 100] });","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await zoom({ region }) } catch (e) {\n  if (String(e.message).includes(\"no screenshot taken yet\")) {\n    await screenshot();\n    await zoom({ region });\n  } else throw e;\n}","preventionTips":["Always screenshot before zoom in the same backend session.","After any backend restart, treat lastRaster as empty.","Pass an explicit `source` path when the raster may be from an earlier session."],"tags":["macos","zoom","invalid-state"],"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-23T08:17:48.524Z"}