{"record":{"id":"7a20d38f407d5983","repo":"Hmbown/CodeWhale","slug":"no-display-disp-have-displays-map-x-x-index-join-screenshot","errorCode":null,"errorMessage":"no display ${disp}; have [${displays.map((x) => x.index).join(\", \")}] — screenshot takes the display index from list_displays, not its id","messagePattern":"no display (.+?); have \\[(.+?)\\] — screenshot takes the display index from list_displays, not its id","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/darwin.mjs","lineNumber":535,"sourceCode":"    // `.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      }\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;","sourceCodeStart":517,"sourceCodeEnd":553,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/darwin.mjs#L517-L553","documentation":"The darwin computer-use screenshot action validates the requested display against the index field of entries returned by list_displays/displayInfo. Callers that pass a display `id` (a macOS CGDisplay UUID) instead of the numeric `index` fail this check. The library throws early so a wrong display value cannot silently produce points/scale that mis-target all later coordinate actions.","triggerScenarios":"Calling screenshot({ display: <display id> }) where the value came from the `id` field of list_displays output rather than its numeric `index`; also triggered by a stale index after displays were plugged/unplugged, or by any non-'all' value not found in displays[].index.","commonSituations":"Copy-pasting a display id from list_displays JSON into the screenshot call; an LLM agent confusing `id` with `index`; hardware display changes making a previously valid index disappear; passing a 0-based index to a 1-based list.","solutions":["Re-run list_displays and pass the `index` field (not `id`) as the screenshot display argument.","If you don't need a specific display, omit `display` entirely or pass \"all\" to capture everything.","After a monitor connect/disconnect, refresh the display list before retrying.","Note that window captures ignore `display` entirely — use window_id instead if you meant to capture a window."],"exampleFix":"// before\nawait screenshot({ display: displays[0].id });\n// after\nawait screenshot({ display: displays[0].index });","handlingStrategy":"validation","validationCode":"const displays = await list_displays();\nif (display != null && display !== \"all\" && !displays.some(d => d.index === display))\n  throw new Error(`use display index from list_displays, not id: ${display}`);","typeGuard":"const isDisplayIndex = (v, displays) => v === \"all\" || displays.some(d => d.index === v);","tryCatchPattern":"try { await screenshot({ display }) } catch (e) {\n  if (String(e.message).startsWith(\"no display\")) {\n    const displays = await list_displays();\n    await screenshot({ display: displays[0].index });\n  } else throw e;\n}","preventionTips":["Always take the screenshot display argument from list_displays `index`, never `id`.","Refresh the display list after any monitor connect/disconnect.","Use window_id for window captures — display is ignored there anyway."],"tags":["macos","screenshot","invalid-argument"],"backgroundTag":"invalid-argument-value","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"}