{"record":{"id":"4314d5a5b6c6d42f","repo":"Hmbown/CodeWhale","slug":"state-wrong-computer","errorCode":"state_wrong_computer","errorMessage":"state_id \"${stateId}\" belongs to computer \"${state.computerId}\", not \"${computer.id}\" — call get_app_state on that computer again","messagePattern":"state_id \"(.+?)\" belongs to computer \"(.+?)\", not \"(.+?)\" — call get_app_state on that computer again","errorType":"error_code","errorClass":"ServerError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/mcp/server.mjs","lineNumber":206,"sourceCode":" * moved-but-identical elements are re-aimed at their fresh center\n * (sink.reacquired = true so the receipt can say target_reacquired).\n */\nasync function normalizeTarget(computer, target, kind, resolve, sink) {\n  if (target?.type === \"coordinate\") {\n    if (target.space === \"screen\") {\n      if (!Number.isFinite(target.x) || !Number.isFinite(target.y)) {\n        throw new ServerError(\"bad_target\", \"screen coordinates must be finite numbers\");\n      }\n      return { x: Math.round(target.x), y: Math.round(target.y), strategy: \"event\", coordinate_space: \"screen\" };\n    }\n    if (target.x < 0 || target.y < 0) throw new ServerError(\"bad_target\", \"raster coordinates must be non-negative\");\n    const pt = rasterToPoints(computer.id, target.x, target.y);\n    return { x: Math.round(pt.x), y: Math.round(pt.y), strategy: \"event\", coordinate_space: \"raster\" };\n  }\n  if (target?.type === \"element\") {\n    const { state, element, stateId } = resolveElement(target, computer);\n    if (state.computerId && state.computerId !== computer.id) {\n      throw new ServerError(\"state_wrong_computer\", `state_id \"${stateId}\" belongs to computer \"${state.computerId}\", not \"${computer.id}\" — call get_app_state on that computer again`);\n    }\n    // The receipt must name the observation actually resolved — a bare index\n    // binds the computer's latest state, so reporting `target.state_id` would\n    // say \"undefined\" for the common case.\n    const where = `state ${stateId} (${state.app_ref?.name ?? state.app_ref?.bundle_id ?? `pid ${state.app_ref?.pid}`})`;\n    let fresh = null;\n    if (resolve) {\n      const res = await resolve({ app_ref: state.app_ref, windowIndex: element.windowIndex ?? 0, path: element.path });\n      if (!res?.found || !res.element) {\n        throw new ServerError(\"element_stale\", `element ${target.index} of ${where} no longer resolves (${res?.reason ?? \"not_found\"}) — the user or the app may have changed it; call get_app_state again`);\n      }\n      fresh = res.element;\n      if (fresh.role !== element.role) {\n        throw new ServerError(\"element_stale\", `element ${target.index} of ${where} changed role (${element.role} → ${fresh.role}) — call get_app_state again`);\n      }\n      // In-place replacement: same role and geometry but a different label is\n      // still a different element (e.g. \"Load\" → \"Confirm\").\n      if (fresh.label !== element.label) {","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/mcp/server.mjs#L188-L224","documentation":"After resolveElement succeeds for an element target, normalizeTarget checks the resolved state's owning computer: if state.computerId differs from the computer the action is addressed to, it throws code \"state_wrong_computer\". Element indexes are only meaningful within the observation that produced them, so using a state captured on machine A to act on machine B is refused outright.","triggerScenarios":"target.type === \"element\" where target.state_id (or the computer's latest state) resolves to a state whose state.computerId !== computer.id — e.g. an agent managing multiple VMs passes the wrong computerId alongside a state_id from another machine.","commonSituations":"Multi-computer automation loops with mismatched bookkeeping between the observation step and the action step; copy-pasted tool calls where only the computer id was edited; parallel agents each holding states for several machines and crossing wires.","solutions":["Address the action to the computer the state came from, or call get_app_state on the intended computer and use its state_id.","Track (computerId, state_id) pairs together in your agent loop; never split them.","Check for computer-id typos or stale variables in multi-machine scripts.","If you genuinely want the same element on another machine, observe that machine and match by role/label, not by index."],"exampleFix":"// before\nawait click({ computerId: \"vm-b\", target: { type: \"element\", state_id: stateFromVmA, index: 2 } });\n\n// after\nconst stB = await getAppState(\"vm-b\");\nawait click({ computerId: \"vm-b\", target: { type: \"element\", state_id: stB.state_id, index: 2 } });","handlingStrategy":"type-guard","validationCode":"const obs = observations.get(stateId);\nif (!obs || obs.computerId !== computerId) throw new Error(`state ${stateId} does not belong to computer ${computerId}`);","typeGuard":"const stateBelongsTo = (stateId, computerId) => observations.get(stateId)?.computerId === computerId;","tryCatchPattern":"try {\n  return await act({ computerId, target: { type: \"element\", state_id, index } });\n} catch (e) {\n  if (e.code === \"state_wrong_computer\") {\n    const owner = e.extra?.computerId ?? ownerOf(stateId);\n    return await act({ computerId: owner, target: { type: \"element\", state_id, index } });\n  }\n  throw e;\n}","preventionTips":["Store (computerId, state_id) as a pair and always pass both from the same record.","In multi-machine loops, resolve observations per computer; never share state_ids across machines.","Assert the computer id at the call site, not deep in a helper.","To act on a different machine, observe it and match elements by role/label."],"tags":["multi-computer","state-management","mcp","mismatch"],"backgroundTag":"type-mismatch","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"}