{"record":{"id":"e34196119ac3fd62","repo":"can1357/oh-my-pi","slug":"timed-out-waiting-for-prior-agent-run-to-finish-be","errorCode":null,"errorMessage":"Timed out waiting for prior agent run to finish before prompting.","messagePattern":"Timed out waiting for prior agent run to finish before prompting\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/session/turn-recovery.ts","lineNumber":2478,"sourceCode":"\t */\n\tabortRetry(): void {\n\t\tthis.#retryAbortController?.abort();\n\t\t// Note: _retryAttempt is reset in the catch block of _autoRetry\n\t\tthis.resolveRetry();\n\t}\n\n\tasync #promptAgentWithIdleRetry(messages: AgentMessage[], options?: { toolChoice?: ToolChoice }): Promise<void> {\n\t\tconst deadline = Date.now() + 30_000;\n\t\tfor (;;) {\n\t\t\ttry {\n\t\t\t\tawait this.#host.agent.prompt(messages, options);\n\t\t\t\treturn;\n\t\t\t} catch (err) {\n\t\t\t\tif (!(err instanceof AgentBusyError)) {\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\t\t\t\tif (Date.now() >= deadline) {\n\t\t\t\t\tthrow new Error(\"Timed out waiting for prior agent run to finish before prompting.\");\n\t\t\t\t}\n\t\t\t\tawait this.#host.agent.waitForIdle();\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Whether auto-retry is currently in progress */\n\tget isRetrying(): boolean {\n\t\treturn this.#retryPromise !== undefined;\n\t}\n\n\t/** Whether auto-retry is enabled */\n\tget autoRetryEnabled(): boolean {\n\t\treturn this.#host.settings.get(\"retry.enabled\") ?? true;\n\t}\n\n\t/**\n\t * Toggle auto-retry setting.","sourceCodeStart":2460,"sourceCodeEnd":2496,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/session/turn-recovery.ts#L2460-L2496","documentation":"When TurnRecovery needs to prompt but a previous agent run is still active, it catches AgentBusyError and waits for the agent to go idle. If the deadline passes while the prior run is still busy, it gives up with this timeout error rather than deadlocking or interleaving runs.","triggerScenarios":"Prompting while a prior agent run is still executing and waitForIdle() repeatedly returns with the run still active until Date.now() exceeds the deadline; long-running tool call or hung model stream in the previous run.","commonSituations":"User sends a new message while a slow previous turn (long bash command, stuck streaming request) is still running; a background/queued prompt races an in-flight run; an aborted-but-not-cleaned-up run keeps the agent busy forever.","solutions":["Wait for the current run to finish before prompting again, or cancel the in-flight run.","Increase the wait deadline if the prior run is legitimately long.","Investigate why the prior run hangs (network stall, tool awaiting input) and abort it so waitForIdle resolves.","Avoid issuing prompts from multiple callers concurrently; serialize prompt submission."],"exampleFix":"// before\nawait recovery.promptUnlessBusy(msg); // times out while previous run hangs\n// after\nif (agent.isBusy()) await agent.abort(); // clear the stuck run first\nawait recovery.promptUnlessBusy(msg);","handlingStrategy":"try-catch","validationCode":"if (!host.agent.isIdle()) {\n  // decide: wait, abort, or defer the prompt before entering recovery\n}\n","typeGuard":null,"tryCatchPattern":"try {\n  await recovery.prompt(msg);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"Timed out waiting for prior agent run\")) {\n    await host.agent.abort(); // clear the stuck run, then retry once\n    await recovery.prompt(msg);\n  } else throw err;\n}","preventionTips":["Serialize all prompt submission through one caller/queue.","Abort obviously-stuck runs instead of letting them block new turns.","Monitor agent idleness before submitting prompts programmatically.","Keep per-run timeouts on tool calls so runs cannot hang indefinitely."],"tags":["concurrency","timeout","agent-lifecycle"],"backgroundTag":"agent-busy-timeout","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}