{"record":{"id":"36b4ec41544d589b","repo":"stablyai/orca","slug":"terminal-input-is-locked-36b4ec","errorCode":null,"errorMessage":"Terminal input is locked","messagePattern":"Terminal input is locked","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mobile/src/session/pr-ai-triage-launch.ts","lineNumber":40,"sourceCode":"    navigation: 'caller'\n  })\n  if (!created.ok) {\n    throw new Error(created.error?.message || 'Failed to create terminal')\n  }\n  const terminalTab = readMobileReviewCreatedTerminal(created.result)\n  if (!terminalTab) {\n    throw new Error('Created terminal response was invalid')\n  }\n  const sent = await client.sendRequest('terminal.send', {\n    terminal: terminalTab.terminal,\n    text: prompt,\n    enter: true\n  })\n  if (!sent.ok) {\n    throw new Error(sent.error?.message || 'Failed to send prompt')\n  }\n  if (!readMobileReviewTerminalSendAccepted(sent.result)) {\n    throw new Error('Terminal input is locked')\n  }\n}\n","sourceCodeStart":22,"sourceCodeEnd":43,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/src/session/pr-ai-triage-launch.ts#L22-L43","documentation":"Thrown when `terminal.send` succeeded (ok:true) but the host reported the input was NOT accepted - `readMobileReviewTerminalSendAccepted` saw result.send.accepted === false. The host accepts the envelope but refuses the keystrokes because the terminal's input is locked by another owner (a native chat composer owns the line, or an overlay has focus). The reader treats a missing `send` record as accepted, so this only fires on an explicit accepted:false.","triggerScenarios":"Another mobile/native chat session owns the terminal input line; an active permission/overlay dialog on the host swallows input; the terminal is in an alt-screen app that locked input.","commonSituations":"Native chat composer left the input line marked stale/owned; two surfaces (triage + native chat) target the same terminal; host input-lock state out of sync.","solutions":["Close competing native chat on that terminal.","Retry the send (the host may release the lock).","Create a fresh terminal for the triage prompt instead of reusing one.","Check host input-lock state for the terminal handle."],"exampleFix":"// before\nif (!readMobileReviewTerminalSendAccepted(sent.result)) {\n  throw new Error('Terminal input is locked')\n}\n// after - retry once on a fresh terminal before giving up\nif (!readMobileReviewTerminalSendAccepted(sent.result)) {\n  const fresh = await createFreshTerminal(client, worktreeId)\n  await sendPrompt(fresh, prompt)\n}","handlingStrategy":"retry","validationCode":"if (isMobileNativeChatInputStale(terminal)) {\n  await healMobileNativeChatStaleInput({ client, terminal, deviceToken: null })\n}","typeGuard":"function readMobileReviewTerminalSendAccepted(value: unknown): boolean {\n  if (!isRecord(value) || !isRecord(value.send)) return true\n  return value.send.accepted !== false\n}","tryCatchPattern":"try {\n  await createTerminalAndSendPrompt(client, worktreeId, prompt)\n} catch (err) {\n  if (/locked/.test(err instanceof Error ? err.message : '')) {\n    setTriageError('Terminal busy - close other chats and retry')\n  } else throw err\n}","preventionTips":["Heal stale native-chat input before sending.","Prefer a freshly-created terminal for one-shot triage prompts.","Avoid targeting a terminal owned by an active native chat composer."],"tags":["terminal-send","input-lock","pr-triage","mobile"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}