{"record":{"id":"e1023e8d3186226a","repo":"Hmbown/CodeWhale","slug":"control-stopped","errorCode":"control_stopped","errorMessage":"stop_computer_control is active; no further actions are permitted this session","messagePattern":"stop_computer_control is active; no further actions are permitted this session","errorType":"error_code","errorClass":"ServerError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/mcp/server.mjs","lineNumber":957,"sourceCode":"              ex.closeChannel?.();\n              return ex.remote(request, opts);\n            }\n            throw err;\n          }\n        }\n        return ex.remote(request, opts);\n      };\n      const resolve = async (req) => {\n        const rep = await remoteCall({ tool: \"resolve_element\", args: req }, { timeoutMs: 30_000 });\n        if (!rep?.ok) return { found: false, element: null, reason: rep?.error?.code ?? \"remote_error\" };\n        return rep.data;\n      };\n      const wireArgs = await prepareArgs(computer, name, args, resolve, sink);\n      throwIfAborted();\n      await assertCurrentRoute(computer, binding);\n      // Re-check the kill switch: a stop that arrived while the executor was\n      // being resolved still blocks this dispatch.\n      if (controlStopped && !READ_ONLY_TOOLS.has(name)) throw new ServerError(\"control_stopped\", \"stop_computer_control is active; no further actions are permitted this session\");\n      inFlight++;\n      try {\n        dispatched = true;\n        const timeoutMs = backendMethod.startsWith(\"recording\") || backendMethod === \"get_app_state\" ? 60_000 : 30_000;\n        const invoke = async (tool, a) => {\n          const r = await remoteCall({ tool, args: a }, { timeoutMs });\n          if (!r.ok) throw new ServerError(r.error?.code ?? \"remote_error\", r.error?.message ?? \"remote agent failed\");\n          return r.data;\n        };\n        data = name === \"type\" ? await invokeType(invoke, wireArgs) : await invoke(backendMethod, wireArgs);\n      } finally {\n        inFlight--;\n      }\n      await assertCurrentRoute(computer, binding, true);\n      if (Array.isArray(data)) data = { items: data };\n      if ((backendMethod === \"screenshot\" || backendMethod === \"zoom\") && data?.file) {\n        if (ex.filesLocal) bindRaster(computer, data);\n        else {","sourceCodeStart":939,"sourceCodeEnd":975,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/mcp/server.mjs#L939-L975","documentation":"stop_computer_control is a session kill switch. Once active, the dispatch loop re-checks it before every tool call and refuses any non-read-only tool with control_stopped, including actions already mid-preparation when stop arrived. Read-only tools (e.g. screenshot, get_app_state) remain allowed by READ_ONLY_TOOLS.","triggerScenarios":"Any mutating computer-use call (click, type, key, run_actions, app_script, ...) issued after stop_computer_control was called in the session; a stop racing with an in-flight prepareArgs resolves first and still blocks dispatch.","commonSituations":"Automations that finished their stop but retried; a user pressing a stop/emergency control then a queued step firing; long-lived sessions where an earlier stop is forgotten.","solutions":["Re-initialize or restart the computer-control session to clear the kill switch if further actions are genuinely needed","Remove subsequent mutating steps after stop_computer_control in any queued run_actions batch","Restrict post-stop work to read-only tools (screenshot, get_app_state) that READ_ONLY_TOOLS permits"],"exampleFix":"// before\nawait stopComputerControl();\nawait click({ target: el }); // throws control_stopped\n// after\nawait stopComputerControl();\n// only read-only calls afterwards, or start a new session before mutating calls","handlingStrategy":"try-catch","validationCode":"if (controlStopped && !READ_ONLY_TOOLS.has(toolName)) throw new Error(\"session control stopped; refusing \" + toolName);","typeGuard":"const isReadOnlyTool = (name) => READ_ONLY_TOOLS.has(name);","tryCatchPattern":"try { await callTool(name, args) } catch (e) { if (e.code === \"control_stopped\") { /* stop the queue; do not retry mutating calls; restart session if actions are still required */ } else throw e; }","preventionTips":["Drain or discard queued actions when stop_computer_control is issued","Track a local stopped flag mirroring the server's kill switch","Only schedule read-only work after a stop"],"tags":["mcp","computer-use","kill-switch"],"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-22T16:17:23.217Z"}