{"record":{"id":"02fc87b6a8e568e1","repo":"Hmbown/CodeWhale","slug":"choose-app-ref-or-region-not-both","errorCode":null,"errorMessage":"choose app_ref or region, not both","messagePattern":"choose app_ref or region, not both","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/darwin.mjs","lineNumber":525,"sourceCode":"  async function screenshot({ display, region, app_ref, window_id, path: outPath } = {}) {\n    // Once an app is selected, ordinary observations follow it behind the\n    // user's work. An explicit display/region remains a deliberate desktop capture.\n    if (app_ref === undefined && display === undefined && region === undefined) app_ref = state.inputApp ?? undefined;\n    const dir = recordingsDir();\n    fs.mkdirSync(dir, { recursive: true });\n    // JPEG, not PNG. A screen is photographic content — gradients, wallpaper,\n    // antialiased text — and lossless compression of it is enormous: the same\n    // 5760x3240 frame is 21.8MB as PNG and 2.1MB as JPEG, at full resolution\n    // and with terminal text still crisp. PNG stays available by asking for a\n    // `.png` path, which is what a pixel-exact comparison wants.\n    const file = outPath || path.join(dir, `shot-${new Date().toISOString().replace(/[:.]/g, \"-\")}-${crypto.randomBytes(3).toString(\"hex\")}.jpg`);\n    if (!/\\.(png|jpe?g)$/i.test(file)) throw new ExecError(\"screenshot path must end in .png, .jpg or .jpeg\");\n    const args = [\"-x\", \"-t\", /\\.png$/i.test(file) ? \"png\" : \"jpg\"];\n    const disp = display ?? state.activeDisplay;\n    // An explicit app reference resolves first and alone: nothing may run\n    // before it and redirect the capture to another target.\n    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      }","sourceCodeStart":507,"sourceCodeEnd":543,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/darwin.mjs#L507-L543","documentation":"A screenshot capture may target either a specific application window (app_ref/window_id) or a screen region — not both, since an explicit app reference resolves alone and must not be redirected by a region. Supplying both is rejected to keep the capture target unambiguous.","triggerScenarios":"Calling screenshot with both app_ref and region set, e.g. copying parameters from two different call paths into one invocation, or a generic wrapper that forwards all optional fields unconditionally.","commonSituations":"Tool wrappers spreading user options without filtering null/undefined; scripts that combine 'capture this app' with 'crop to region' expectations — cropping must be done as a post-processing step.","solutions":["Pass either app_ref/window_id or region, not both","Capture the app window and crop to the region afterwards if both constraints are needed","In generic wrappers, omit falsy/undefined optional fields before calling","Use display + region for screen-area captures, or app_ref alone for window captures"],"exampleFix":"// before\nawait screenshot({ appRef: app.pid, region: { x: 0, y: 0, w: 100, h: 100 } });\n// after\nawait screenshot({ appRef: app.pid });","handlingStrategy":"validation","validationCode":"if (appRef !== undefined && region) throw new Error(\"appRef and region are mutually exclusive\");","typeGuard":null,"tryCatchPattern":"try {\n  await screenshot(opts);\n} catch (e) {\n  if (e instanceof ExecError && e.message.includes(\"choose app_ref or region\")) {\n    // drop region and retry window-scoped, or drop appRef and retry region-scoped\n  } else throw e;\n}","preventionTips":["In generic wrappers, strip undefined/empty optional fields before forwarding","Model the two capture modes as separate tool operations so they cannot collide","Crop window captures as post-processing instead of passing region alongside app_ref"],"tags":["screenshot","mutually-exclusive","validation"],"backgroundTag":"mutually-exclusive-options","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"}