{"record":{"id":"b1fd9fd5db5568c6","repo":"Hmbown/CodeWhale","slug":"input-is-still-being-released-try-again-in-a-moment","errorCode":null,"errorMessage":"Input is still being released; try again in a moment.","messagePattern":"Input is still being released; try again in a moment\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"crates/tui/plugins/computer-use/src/app-handler.mjs","lineNumber":63,"sourceCode":"\nconst sessions = new Map();\nlet controlMode = \"ready\";\nlet controlGeneration = 0;\nlet cleanupPending = false;\n\n/** Human-facing state contains app identity and action names, never task text. */\nexport function controlStatus() {\n  return { mode: controlMode, cleanupPending, sessions: [...sessions.values()]\n    .filter((s) => !s.closed && s.target)\n    .map((s) => ({ target: s.target, mode: s.mode, action: s.action ?? null })) };\n}\n\n// Called only by the launcher's inherited control channel, never an MCP tool.\n// Abort before queuing cleanup so even a held gesture yields to the person.\nexport async function setControlMode(mode) {\n  if (![\"ready\", \"paused\", \"stopped\"].includes(mode)) throw new Error(\"Unknown control mode\");\n  if (mode === \"ready\") {\n    if (cleanupPending) throw new Error(\"Input is still being released; try again in a moment.\");\n    controlMode = mode;\n    return controlStatus();\n  }\n  controlMode = mode;\n  const generation = ++controlGeneration;\n  cleanupPending = true;\n  const results = await Promise.allSettled([...sessions.keys()].map((key) => {\n    const colon = key.indexOf(\":\");\n    return releaseSessionInput(key.slice(colon + 1), key.slice(0, colon), { close: mode === \"stopped\" });\n  }));\n  const failure = results.find((r) => r.status === \"rejected\");\n  // A cleanup failure stays blocked. Resume cannot hide owned input.\n  if (failure) throw failure.reason;\n  if (generation === controlGeneration) cleanupPending = false;\n  return controlStatus();\n}\n\nfunction enqueue(fn) {","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/app-handler.mjs#L45-L81","documentation":"When switching the control mode to \"ready\", `setControlMode` first checks whether a previous cleanup (release of injected input, gesture abort) is still in progress (`cleanupPending`). If so it refuses with \"Input is still being released; try again in a moment.\" This ordering guarantees even a held synthetic gesture yields to the human before input control is handed back.","triggerScenarios":"Sending mode \"ready\" while a prior \"stopped\" transition is still running its cleanup: e.g. a supervisor sends paused→stopped→ready in rapid succession, or a launcher auto-restarts and immediately re-enables input while the previous teardown is mid-flight.","commonSituations":"Fast restart loops, watchdog scripts that toggle modes without waiting for the ready acknowledgement, or a user interrupting input injection and immediately retrying.","solutions":["Wait for the cleanup to finish (poll controlStatus / await the control-channel acknowledgement) and resend \"ready\".","Add a short retry with backoff when receiving this message.","Have the supervisor wait for the \"ready\" transition to be confirmed before issuing further commands.","Investigate why cleanup is slow if the condition persists (stuck gesture release)."],"exampleFix":"// before\\nawait setControlMode(\"stopped\");\\nawait setControlMode(\"ready\"); // throws if cleanup pending\\n// after\\nawait setControlMode(\"stopped\");\\nawait waitForControlStatus(s => !s.cleanupPending);\\nawait setControlMode(\"ready\");","handlingStrategy":"retry","validationCode":"const st = await controlStatus();\\nif (st.cleanupPending) await sleep(100); // before requesting ready","typeGuard":null,"tryCatchPattern":"try { await setControlMode(\"ready\"); } catch (e) { if (/still being released/.test(e.message)) { await sleep(200); return setControlMode(\"ready\"); } throw e; }","preventionTips":["Wait for the mode transition to be acknowledged before issuing the next command.","Never fire paused/stopped/ready transitions back-to-back without awaiting.","Add bounded backoff retry around ready transitions."],"tags":["state-machine","retry","computer-use"],"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"}