{"record":{"id":"ee77d904a4ad33af","repo":"stablyai/orca","slug":"terminal-input-is-locked-ee77d9","errorCode":null,"errorMessage":"Terminal input is locked","messagePattern":"Terminal input is locked","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mobile/src/session/use-mobile-diff-review-send-actions.ts","lineNumber":92,"sourceCode":"    async (terminal: string, comments: readonly DiffComment[]) => {\n      if (!client || connState !== 'connected') {\n        throw new Error('Waiting for desktop...')\n      }\n      // Marked by terminal handle, not by surface, so a paste orphaned here by native\n      // chat would ride along with these notes (#10228). Diff review carries no device token.\n      if (!(await healMobileNativeChatStaleInput({ client, terminal, deviceToken: null }))) {\n        throw new Error('Failed to send notes')\n      }\n      const response = await client.sendRequest('terminal.send', {\n        terminal,\n        text: formatMobileDiffReviewPrompt(comments),\n        enter: true\n      })\n      if (!response.ok) {\n        throw new Error(response.error?.message || 'Failed to send notes')\n      }\n      if (!readMobileReviewTerminalSendAccepted(response.result)) {\n        throw new Error('Terminal input is locked')\n      }\n      await markNotesSent(comments)\n      triggerSuccess()\n      setActionError('Review notes sent')\n      setSendSheet(null)\n    },\n    [client, connState, markNotesSent, setActionError, setSendSheet]\n  )\n\n  const createTerminalAndSend = useCallback(\n    async (comments: readonly DiffComment[]) => {\n      if (!client || connState !== 'connected') {\n        throw new Error('Waiting for desktop...')\n      }\n      const response = await client.sendRequest('session.tabs.createTerminal', {\n        worktree: `id:${worktreeId}`,\n        activate: false,\n        select: true,","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/src/session/use-mobile-diff-review-send-actions.ts#L74-L110","documentation":"Same reader as 386 but on the notes-send path: `terminal.send` succeeded (ok:true) yet result.send.accepted === false. The host accepted the envelope but refused the keystrokes because the terminal input is owned/locked.","triggerScenarios":"A native chat composer or another surface owns the terminal input line; an overlay has focus; the stale-input heal consumed the marker but the lock returned.","commonSituations":"Native chat left the line owned; two send surfaces target the same terminal; host lock state stale.","solutions":["Close competing native chat on that terminal.","Retry the send.","Pick a different terminal / create a fresh one.","Heal stale input again."],"exampleFix":"// before\nif (!readMobileReviewTerminalSendAccepted(response.result)) {\n  throw new Error('Terminal input is locked')\n}\n// after - fall back to a fresh terminal\nif (!readMobileReviewTerminalSendAccepted(response.result)) {\n  return createTerminalAndSend(comments)\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 sendPromptToTerminal(terminal, comments)\n} catch (err) {\n  if (/locked/.test(err instanceof Error ? err.message : '')) {\n    return createTerminalAndSend(comments)\n  }\n  throw err\n}","preventionTips":["Heal stale input before sending.","Prefer freshly-created terminals.","Avoid terminals owned by active native chat."],"tags":["terminal-send","input-lock","send-notes","diff-review","mobile"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}