{"record":{"id":"a9efa828685eed3e","repo":"Hmbown/CodeWhale","slug":"computer-route-changed","errorCode":"computer_route_changed","errorMessage":"Computer route changed during this request — observe the registered target again before acting","messagePattern":"Computer route changed during this request — observe the registered target again before acting","errorType":"error_code","errorClass":"ServerError","httpStatus":null,"severity":"warning","filePath":"crates/tui/plugins/computer-use/mcp/server.mjs","lineNumber":139,"sourceCode":"  if (binding && (binding.route !== route || binding.retired)) {\n    await retireBinding(computer.id);\n    binding = { route, needsObservation: true };\n    backendCache.set(computer.id, binding);\n  } else if (!binding) {\n    binding = { route, needsObservation: false };\n    backendCache.set(computer.id, binding);\n  }\n  return binding;\n}\n\nasync function assertCurrentRoute(computer, binding, dispatched = false) {\n  try {\n    let current;\n    try { current = registry.get(computer.id); }\n    catch (err) { await retireBinding(computer.id); throw err; }\n    if (binding.retired || routeFingerprint(current) !== binding.route) {\n      await bindComputer(current);\n      throw new ServerError(\"computer_route_changed\", \"Computer route changed during this request — observe the registered target again before acting\");\n    }\n  } catch (err) {\n    if (dispatched) err.requestDispatched = true;\n    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 */","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/mcp/server.mjs#L121-L157","documentation":"assertCurrentRoute() re-checks, per request, that the registered computer's route fingerprint still matches the fingerprint recorded in the binding when it was first observed. If the binding was retired or the registry entry now routes elsewhere, it rebinds to the current target and throws a ServerError with code computer_route_changed, forcing the caller to re-observe and act on the new target rather than the stale one.","triggerScenarios":"The computer identified by computer.id was unregistered and re-registered with a different route between when the binding was captured and when the tool call dispatched; registry.get() returns a current entry whose routeFingerprint differs from binding.route; the binding was retired concurrently; the underlying target moved (reconnected session, changed host/port/display identity).","commonSituations":"A long-running agent workflow holding a stale computer reference while the target session was restarted or re-registered; VM/remote machine reconnected with a new route; two clients racing where one re-registered the computer; load balancer or session manager reassigning the backing target mid-conversation.","solutions":["Re-observe the registered target: list/get the computer by id from the registry and re-read its current route, then retry the action against the new target.","Discard any cached computer handle or binding from before the change; rebind (the server already called bindComputer) and rebuild state from the fresh observation.","If your workflow pinned a specific route, verify the re-registered computer is the same machine you expect before re-issuing input actions.","Retry the request after re-observation; if it recurs constantly, reduce the time between registration and use or investigate what is re-registering the target."],"exampleFix":"// before\nconst computer = getStaleCachedComputer(id);\nawait act(computer, action);\n// after\nlet computer;\ntry { computer = await act(cached, action); }\ncatch (err) {\n  if (err.code === \"computer_route_changed\") {\n    computer = await registry.get(id); // re-observe current target\n    await act(computer, action);\n  } else throw err;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await callComputerTool(computer, action); }\ncatch (err) {\n  if (err.code === 'computer_route_changed') {\n    const current = await registry.get(computer.id); // re-observe\n    await callComputerTool(current, action);\n  } else throw err;\n}","preventionTips":["Never cache computer handles across re-registrations; resolve from the registry at point of use.","Re-observe the target after any session restart, reconnect, or long idle period.","Treat err.requestDispatched === true carefully — the request may have reached the old target before the change was detected."],"tags":["stale-reference","concurrency","routing","mcp"],"backgroundTag":"invalid-state-transition","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"}