{"record":{"id":"787c6ce4826ad529","repo":"Hmbown/CodeWhale","slug":"unknown-element","errorCode":"unknown_element","errorMessage":"element index ${target.index} is outside state ${stateId} (0..${st.elements.length - 1})","messagePattern":"element index (.+?) is outside state (.+?) \\(0\\.\\.(.+?)\\)","errorType":"error_code","errorClass":"ServerError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/mcp/server.mjs","lineNumber":165,"sourceCode":"async function getBackend(computer, binding) {\n  if (!binding.backend) binding.backend = (await backendFor(computer)).backend;\n  return binding.backend;\n}\n\n/**\n * Element target -> enriched target with cached app identity and AX path.\n * An explicit state_id pins a specific observation; a bare index addresses\n * the latest observation on this computer — the flat addressing a caller\n * uses when it acts on what it just saw.\n */\nfunction resolveElement(target, computer) {\n  const stateId = target.state_id ?? latestStateByComputer.get(computer.id);\n  const st = stateId ? appStates.get(stateId) : null;\n  if (!st) throw new ServerError(\"unknown_state\", target.state_id\n    ? `state_id \"${target.state_id}\" is unknown or expired — call get_app_state again`\n    : \"no observation on this computer yet — call get_app_state first\");\n  const el = st.elements[target.index];\n  if (!el) throw new ServerError(\"unknown_element\", `element index ${target.index} is outside state ${stateId} (0..${st.elements.length - 1})`);\n  return { state: st, element: el, stateId };\n}\n\nclass ServerError extends Error {\n  constructor(code, message, extra = null) { super(message); this.code = code; if (extra) this.extra = extra; }\n}\n\n/** Map raster-pixel coordinates to screen points using the bound raster. */\nfunction rasterToPoints(computerId, x, y) {\n  const r = lastRasters.get(computerId);\n  if (!r) throw new ServerError(\"no_raster\", \"no screenshot bound on this computer yet — call screenshot first so pixel targets have a frame\");\n  if (r.pixels?.w != null && r.pixels?.h != null && (x < 0 || y < 0 || x >= r.pixels.w || y >= r.pixels.h)) {\n    throw new ServerError(\"target_outside_raster\", `target (${x},${y}) is outside the bound raster (${r.pixels.w}x${r.pixels.h} pixels) — take a fresh screenshot`);\n  }\n  const scale = r.scale && r.scale > 0 ? r.scale : 1;\n  return { x: (r.origin?.x ?? 0) + x / scale, y: (r.origin?.y ?? 0) + y / scale };\n}\n","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/mcp/server.mjs#L147-L183","documentation":"After resolving the state, resolveElement indexes st.elements[target.index] and throws code \"unknown_element\" when that slot is empty — the index is outside the element list of the referenced state (valid range 0..length-1). The message includes both the state_id and the valid range so the caller can correct the index against the observation it holds.","triggerScenarios":"target.type === \"element\" with an index >= st.elements.length or < 0 (negative indexes hit holes, not the last element). Typical when an index from an older observation is reused against a different/smaller state, or the index is 1-based from a human-facing list pasted into a 0-based API.","commonSituations":"Mixing index from state A with state_id from state B; off-by-one after the UI shrank between turns; copy-pasting element numbers from a rendered accessibility tree that numbers from 1; loops that run past the end of the elements array.","solutions":["Clamp/validate the index against the observation you hold: 0 <= index < elements.length before sending.","Re-run get_app_state and re-resolve the element by its role/label in the fresh observation; indexes shift when UI changes.","If your list is 1-based, subtract 1 before sending.","Pair the index with the state_id it actually came from — never combine an index and state_id from different observations."],"exampleFix":"// before\nconst idx = Number(uiRow); // 1-based from a rendered list\nawait act({ type: \"element\", state_id: st.state_id, index: idx });\n\n// after\nconst idx = Number(uiRow) - 1;\nif (idx < 0 || idx >= st.elements.length) throw new Error(`row ${uiRow} not in observation`);\nawait act({ type: \"element\", state_id: st.state_id, index: idx });","handlingStrategy":"validation","validationCode":"if (!Number.isInteger(index) || index < 0 || index >= observation.elements.length) throw new Error(`element index ${index} out of range 0..${observation.elements.length - 1}`);","typeGuard":"const isValidIndex = (obs, i) => obs && Array.isArray(obs.elements) && Number.isInteger(i) && i >= 0 && i < obs.elements.length;","tryCatchPattern":"try {\n  return await act({ type: \"element\", state_id, index });\n} catch (e) {\n  if (e.code === \"unknown_element\") {\n    const fresh = await getAppState(computerId);\n    return await act({ type: \"element\", state_id: fresh.state_id, index: relocate(fresh, index) });\n  }\n  throw e;\n}","preventionTips":["Always validate the index against the same observation the state_id came from.","Remember indexes are 0-based; convert any 1-based human-facing numbering.","Re-locate elements by role/label after UI changes instead of trusting old indexes.","Never mix an index from one state with a state_id from another."],"tags":["indexing","bounds-check","mcp","computer-use"],"backgroundTag":"index-out-of-bounds","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"}