{"record":{"id":"4239dd3ee9d46f96","repo":"Hmbown/CodeWhale","slug":"region-must-be-x-y-w-h-in-last-raster-pixels","errorCode":null,"errorMessage":"region must be [x, y, w, h] in last-raster pixels","messagePattern":"region must be \\[x, y, w, h\\] in last-raster pixels","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/darwin.mjs","lineNumber":574,"sourceCode":"      ...(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) {\n    if (r.child.exitCode == null && r.child.signalCode == null) {\n      r.child.stdin.end();","sourceCodeStart":556,"sourceCodeEnd":592,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/darwin.mjs#L556-L592","documentation":"zoom validates its `region` as exactly 4 non-negative integers in PIXELS of the last raster (not screen points), requiring w>0, h>0, and x+w / y+h within the raster's pixel bounds. Non-integer, empty, negative, or out-of-bounds regions all throw this error.","triggerScenarios":"Passing screen-point coordinates (from mouse position or window rects) instead of pixel coordinates; region extends past the screenshot's pixel width/height (e.g. after fitRasterToBudget downscaled the capture); fractional/float coordinates; w or h equal to 0.","commonSituations":"Retina displays where points != pixels (2x scale) causing out-of-bounds regions; the screenshot was downscaled to fit a raster budget so old coordinates no longer fit; agent emits floats like 12.5.","solutions":["Use the `pixels.w`/`pixels.h` values returned by the previous screenshot to clamp the region: x+w <= pixels.w and y+h <= pixels.h.","Convert screen points to raster pixels by multiplying by the screenshot's reported scale when necessary.","Round all four values with Math.round/trunc to integers and ensure w,h > 0.","Take a fresh screenshot if you suspect the raster was resized since you computed coordinates."],"exampleFix":"// before\nawait zoom({ region: [10.5, 0, 800, 600] }); // points on a 1x-scaled 1200x800 raster\n// after\nawait zoom({ region: [10, 0, 700, 500] }); // integers within pixels.w/h","handlingStrategy":"validation","validationCode":"const shot = await screenshot();\nconst [x, y, w, h] = region.map(Math.round);\nif (w <= 0 || h <= 0 || x < 0 || y < 0 || x + w > shot.pixels.w || y + h > shot.pixels.h)\n  throw new Error(`region must fit within ${shot.pixels.w}x${shot.pixels.h} pixels`);","typeGuard":"const isPixelRegion = (r, raster) => Array.isArray(r) && r.length === 4 &&\n  r.every(n => Number.isInteger(n) && n >= 0) && r[2] > 0 && r[3] > 0 &&\n  r[0] + r[2] <= raster.pixels.w && r[1] + r[3] <= raster.pixels.h;","tryCatchPattern":"try { await zoom({ region }) } catch (e) {\n  if (String(e.message).includes(\"last-raster pixels\")) {\n    const { pixels } = await screenshot();\n    const [x, y, w, h] = region.map(n => Math.max(0, Math.round(n)));\n    await zoom({ region: [x, y, Math.min(w, pixels.w - x), Math.min(h, pixels.h - y)] });\n  } else throw e;\n}","preventionTips":["Compute zoom regions in raster pixels, not screen points — check the screenshot's reported scale.","Clamp against pixels.w/pixels.h from the latest screenshot; the raster may have been downscaled to fit a budget.","Use integers only; round or truncate before calling."],"tags":["macos","zoom","validation","coordinate-system"],"backgroundTag":"value-out-of-range","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"}