{"record":{"id":"06d6f7e12b7fcd97","repo":"Hmbown/CodeWhale","slug":"region-must-be-x-y-w-h-in-screen-points","errorCode":null,"errorMessage":"region must be [x, y, w, h] in screen points","messagePattern":"region must be \\[x, y, w, h\\] in screen points","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/darwin.mjs","lineNumber":542,"sourceCode":"    const window = app_ref !== undefined ? await native(\"window_info\", { app_ref, window_id }) : null;\n    if (window && region) throw new ExecError(\"choose app_ref or region, not both\");\n    // On the display path, resolve displays before capturing so an unknown\n    // index is a clean error instead of a raster silently labelled with another\n    // display's geometry — list_displays reports `index` and `id` separately,\n    // and a caller passing the id would otherwise get points and scale that\n    // mis-target every later coordinate. A window capture ignores `display`.\n    let displays = null;\n    if (!window) {\n      displays = await displayInfo();\n      if (disp != null && disp !== \"all\" && !displays.some((x) => x.index === disp)) {\n        throw new ExecError(`no display ${disp}; have [${displays.map((x) => x.index).join(\", \")}] — screenshot takes the display index from list_displays, not its id`);\n      }\n    }\n    if (window) args.push(\"-o\", \"-l\", String(window.window_id));\n    else if (disp && disp !== \"all\") args.push(\"-D\", String(disp));\n    if (region) {\n      if (!region.every((n) => Number.isFinite(n) && n >= 0) || region.length !== 4) {\n        throw new ExecError(\"region must be [x, y, w, h] in screen points\");\n      }\n      args.push(\"-R\", region.join(\",\"));\n    }\n    args.push(file);\n    const r = await runL(\"screencapture\", args, { timeoutMs: 20_000 });\n    if (r.code !== 0) throw new ExecError(`screencapture exited ${r.code}: ${r.stderr.trim().slice(0, 300)}`, r);\n    await fitRasterToBudget(file);\n    const stat = fs.statSync(file);\n    displays ??= await displayInfo();\n    const d = displays.find((x) => x.index === (disp === \"all\" ? 1 : disp)) ?? displays[0];\n    const scale = d?.scale ?? 1;\n    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","sourceCodeStart":524,"sourceCodeEnd":560,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/darwin.mjs#L524-L560","documentation":"The screenshot action validates the optional `region` option: it must be an array of exactly 4 finite, non-negative numbers representing [x, y, w, h] in screen points. The library rejects anything else before shelling out to macOS `screencapture -R`, which expects a comma-separated rect.","triggerScenarios":"Calling screenshot({ region: ... }) with: a region of length != 4; containing NaN/Infinity, negative values, or non-numeric entries (e.g. strings from JSON parsing); or null/undefined array elements.","commonSituations":"Agent emits region as strings ('100','200') or as {x,y,width,height} object instead of a 4-array; accidental negative origin when a window moved off-screen; JSON round-trip turning values into strings.","solutions":["Reshape the region to a strict 4-element numeric array [x, y, w, h] in screen points.","Clamp negative x/y to 0 and coerce all values with Number() before calling.","If you have an {x,y,width,height} object, map it to [x, y, width, height].","Omit `region` if you want the whole display."],"exampleFix":"// before\nawait screenshot({ region: { x: 10, y: 20, width: 300, height: 200 } });\n// after\nawait screenshot({ region: [10, 20, 300, 200] });","handlingStrategy":"validation","validationCode":"const ok = Array.isArray(region) && region.length === 4 && region.every(n => Number.isFinite(n) && n >= 0);\nif (!ok) throw new Error(\"region must be [x, y, w, h] in screen points\");","typeGuard":"const isRegion = (r) => Array.isArray(r) && r.length === 4 && r.every(n => Number.isFinite(n) && n >= 0);","tryCatchPattern":"try { await screenshot({ region }) } catch (e) {\n  if (String(e.message).includes(\"region must be\")) {\n    const [x, y, w, h] = region && typeof region === \"object\" && !Array.isArray(region)\n      ? [region.x, region.y, region.width, region.height] : region;\n    await screenshot({ region: [Math.max(0, Math.round(x)), Math.max(0, Math.round(y)), Math.round(w), Math.round(h)] });\n  } else throw e;\n}","preventionTips":["Normalize {x,y,width,height} objects to 4-arrays before every screenshot call.","Coerce JSON-round-tripped values with Number() to avoid string elements.","Clamp origins to >= 0 when windows can sit partially off-screen."],"tags":["macos","screenshot","validation"],"backgroundTag":"invalid-argument-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}