{"record":{"id":"a73d3413f064c459","repo":"Hmbown/CodeWhale","slug":"display-index-is-out-of-range","errorCode":null,"errorMessage":"display index is out of range","messagePattern":"display index is out of range","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/win32.mjs","lineNumber":281,"sourceCode":"    closeSession: async () => { await browser.close().catch(() => {}); },\n    probe: async () => {\n      const psOk = await ps(\"Write-Output 'ok'\").then((r) => r.code === 0).catch(() => false);\n      return {\n        platform: \"win32\",\n        powershell: psOk,\n        capabilities: { screenshot: psOk, accessibility_tree: psOk, clipboard: psOk, recording: false, raw_input: psOk, held_input: psOk && opts.exec?.persistentInputOwner === true },\n        note: \"Recording is unavailable until session-owned cleanup is implemented; use screenshots. UIA accessibility works without extra installs. Held keys, held buttons and drag require a connected Computer Use desktop helper.\",\n      };\n    },\n    list_displays: async () => {\n      const d = await psJson(`Add-Type -AssemblyName System.Windows.Forms;\n$arr = @([System.Windows.Forms.Screen]::AllScreens | ForEach-Object { [pscustomobject]@{ name = $_.DeviceName; primary = $_.Primary; x = $_.Bounds.X; y = $_.Bounds.Y; w = $_.Bounds.Width; h = $_.Bounds.Height } });\n@{ displays = $arr } | ConvertTo-Json -Depth 4 -Compress;`, { timeoutMs: 15_000 });\n      return d.displays.map((x, i) => ({ index: i + 1, name: x.name, points: { x: x.x, y: x.y, w: x.w, h: x.h }, pixels: { w: x.w, h: x.h }, scale: 1, main: !!x.primary }));\n    },\n    async switch_display({ index = 1 }) {\n      const displays = await this.list_displays();\n      if (!displays.some(d => d.index === index)) throw new ExecError(\"display index is out of range\");\n      activeDisplay = index;\n      return { activeDisplay };\n    },\n    list_apps: async () => {\n      const j = await psJson(`Add-Type -AssemblyName System.Windows.Forms;\n$out = Get-Process | Where-Object { $_.MainWindowTitle } | ForEach-Object { [pscustomobject]@{ name = $_.ProcessName; pid2 = $_.Id; title = $_.MainWindowTitle } } | ConvertTo-Json -Compress;\nif (-not $out) { $out = '[]' }\nWrite-Output ('{\"apps\": ' + $out + '}');`);\n      return { apps: (Array.isArray(j.apps) ? j.apps : [j.apps]).map((a) => ({ name: a.name, pid: a.pid2, title: a.title })) };\n    },\n    list_windows: async (args = {}) => {\n      if (Object.hasOwn(args, \"app_ref\") || Object.hasOwn(args, \"window_id\")) throw unsupportedSelector(\"Windows list_windows does not support app_ref or window_id; omit them to list all windows\");\n      const j = await psJson(`$ErrorActionPreference = 'Stop';\nAdd-Type -AssemblyName System.Windows.Forms;\nAdd-Type -TypeDefinition @'\nusing System;\nusing System.Text;\nusing System.Collections.Generic;","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/win32.mjs#L263-L299","documentation":"switch_display validates the requested 1-based display index against the list produced by list_displays (Screen.AllScreens). It throws this ExecError when no display has that index, so activeDisplay is never set to a nonexistent monitor.","triggerScenarios":"Calling switch_display({index: N}) where N < 1, N > number of connected monitors, or after a monitor was unplugged so the cached index no longer exists.","commonSituations":"Scripts hard-coding a monitor index from another machine; display hot-unplug; single-monitor machines receiving index 2; confusion between 0-based and 1-based indexing.","solutions":["Call list_displays() first and pick an index from its returned 1-based indexes","Remember indexes are 1-based; use 1 for the primary/first display","Catch ExecError and fall back to list_displays to re-enumerate after hardware changes"],"exampleFix":"// before\nawait switchDisplay({ index: 2 });\n// after\nconst { displays } = await listDisplays();\nawait switchDisplay({ index: displays[0].index });","handlingStrategy":"validation","validationCode":"async function safeSwitchDisplay(backend, index) {\n  const { displays } = await backend.list_displays();\n  if (!displays.some(d => d.index === index)) {\n    throw new Error(`display ${index} not found; available: ${displays.map(d => d.index).join(\",\")}`);\n  }\n  return backend.switch_display({ index });\n}","typeGuard":"const isValidDisplayIndex = (v, displays) =>\n  Number.isInteger(v) && v >= 1 && displays.some(d => d.index === v);","tryCatchPattern":"try {\n  await backend.switch_display({ index: 2 });\n} catch (e) {\n  if (String(e.message) === \"display index is out of range\") {\n    const { displays } = await backend.list_displays();\n    await backend.switch_display({ index: displays[0].index });\n  } else throw e;\n}","preventionTips":["Always enumerate with list_displays before switching","Remember indexes are 1-based, never 0","Re-enumerate after monitor hotplug events","Never hard-code monitor indexes across machines"],"tags":["display","validation","windows"],"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-22T11:17:16.035Z"}