{"record":{"id":"c7c869c9769f3a75","repo":"Hmbown/CodeWhale","slug":"unknown-state","errorCode":"unknown_state","errorMessage":"state_id \"${target.state_id}\" is unknown or expired — call get_app_state again","messagePattern":"state_id \"(.+?)\" is unknown or expired — call get_app_state again","errorType":"error_code","errorClass":"ServerError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/mcp/server.mjs","lineNumber":161,"sourceCode":"    throw err;\n  }\n}\n\nasync 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  }","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/mcp/server.mjs#L143-L179","documentation":"resolveElement resolves the flat element addressing a caller saw in its last get_app_state observation. Every observation is stored under a state_id in a bounded in-memory map (appStates), so the server throws code \"unknown_state\" when the supplied state_id is no longer present — it was never valid or has been evicted/expired. The error is thrown before any action is taken so the caller re-observes rather than acting on a blind index.","triggerScenarios":"Any act/click/type-style tool call with target.type === \"element\" where target.state_id is set but appStates.get(state_id) returns null: the id was mistyped, the server restarted, the state cache evicted it after capacity/age limits, or the session was reconnected.","commonSituations":"A long-running agent loop holds state_ids from many turns ago while the server only keeps recent observations; scripts written against one server process replayed against a restarted one; a caller copies a state_id between two computers/sessions; logging/truncation mangles the id.","solutions":["Call get_app_state on the target computer again to obtain a fresh state_id, then immediately act using the new observation.","Re-derive the target element index from the fresh observation (UI may have changed, so don't blindly reuse the old index).","Verify the state_id string is passed through unmodified (no truncation, no interpolation into a template that mangles it).","If states expire quickly in your workflow, interleave get_app_state between actions instead of caching observations across many steps."],"exampleFix":"// before\nawait act({ type: \"element\", state_id: oldStateId, index: 3 });\n\n// after\nconst st = await getAppState(computerId);\nawait act({ type: \"element\", state_id: st.state_id, index: 3 });","handlingStrategy":"retry","validationCode":"if (!st || !stateIds.has(stateId)) { st = await getAppState(computerId); stateId = st.state_id; }","typeGuard":"const hasState = (stateId) => typeof stateId === \"string\" && stateId.length > 0 && knownStateIds.has(stateId);","tryCatchPattern":"try {\n  return await act({ type: \"element\", state_id, index });\n} catch (e) {\n  if (e.code === \"unknown_state\") {\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":["Re-observe (get_app_state) immediately before each action instead of caching state_ids across turns.","Keep state_id and its element indexes together as one immutable observation record.","Re-observe after any server restart or reconnect.","Evict your own cached observations when the server reports capacity/age limits."],"tags":["state-management","expired-state","mcp","computer-use"],"backgroundTag":"record-not-found","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"}