{"record":{"id":"829d31f2cd4b2bde","repo":"stablyai/orca","slug":"a-claude-account-switch-is-already-in-progress","errorCode":null,"errorMessage":"A Claude account switch is already in progress.","messagePattern":"A Claude account switch is already in progress\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/main/claude-accounts/live-pty-gate.ts","lineNumber":90,"sourceCode":"  seededUnconfirmedPtyIds.delete(ptyId)\n  persistence?.addClaudeLivePtySessionId(ptyId)\n}\n\nexport function markClaudePtyExited(ptyId: string): void {\n  const hadLivePtys = liveClaudePtyIds.size > 0\n  liveClaudePtyIds.delete(ptyId)\n  seededUnconfirmedPtyIds.delete(ptyId)\n  persistence?.removeClaudeLivePtySessionId(ptyId)\n  notifyDrainedOnTransition(hadLivePtys)\n}\n\nexport function hasLiveClaudePtys(): boolean {\n  return liveClaudePtyIds.size > 0\n}\n\nexport function beginClaudeAuthSwitch(): void {\n  if (switchInProgress) {\n    throw new Error('A Claude account switch is already in progress.')\n  }\n  switchInProgress = true\n}\n\nexport function endClaudeAuthSwitch(): void {\n  switchInProgress = false\n}\n\nexport function isClaudeAuthSwitchInProgress(): boolean {\n  return switchInProgress\n}\n","sourceCodeStart":72,"sourceCodeEnd":102,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/claude-accounts/live-pty-gate.ts#L72-L102","documentation":"Thrown by beginClaudeAuthSwitch() when the module-level switchInProgress flag is already true. The flag is a process-wide mutex (single boolean) serializing Claude account switches; calling beginClaudeAuthSwitch() a second time before endClaudeAuthSwitch() resets it is rejected. This prevents two concurrent rotations of single-use OAuth refresh tokens.","triggerScenarios":"A second UI action (e.g. user double-clicks 'switch account', or an automation fires selectAccount concurrently) invokes beginClaudeAuthSwitch while a prior switch is still running. endClaudeAuthSwitch was not called because the first switch threw before its finally block.","commonSituations":"Double-click on a switch-account button. Concurrent IPC handlers triggering selection. A prior switch crashed and left switchInProgress stuck true (missing endClaudeAuthSwitch in a finally).","solutions":["Guard the caller with isClaudeAuthSwitchInProgress() before calling beginClaudeAuthSwitch().","Ensure endClaudeAuthSwitch() always runs in a finally block around the switch logic so the flag can never get stuck.","Debounce/coalesce rapid UI switch requests so only one proceeds.","If the flag is stuck true after a crash, surface a 'switch in progress' state to the UI rather than throwing."],"exampleFix":"// before\nexport function beginClaudeAuthSwitch(): void {\n  if (switchInProgress) {\n    throw new Error('A Claude account switch is already in progress.')\n  }\n  switchInProgress = true\n}\n\n// after — caller side\nbeginClaudeAuthSwitch()\ntry {\n  await performSwitch()\n} finally {\n  endClaudeAuthSwitch()\n}","handlingStrategy":"validation","validationCode":"if (isClaudeAuthSwitchInProgress()) {\n  return { status: 'already-in-progress' }\n}\nbeginClaudeAuthSwitch()","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair beginClaudeAuthSwitch with endClaudeAuthSwitch in a finally block.","Check isClaudeAuthSwitchInProgress() before beginning.","Debounce UI switch requests so duplicates coalesce.","Surface 'switch in progress' state to the UI to prevent double-submit."],"tags":["claude-accounts","concurrency","mutex","auth-switch"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}