{"record":{"id":"11412e80f206e7df","repo":"can1357/oh-my-pi","slug":"options-errormessage","errorCode":null,"errorMessage":"options.errorMessage","messagePattern":"options\\.errorMessage","errorType":"exception","errorClass":"AIError.StreamTimeoutError","httpStatus":null,"severity":"warning","filePath":"packages/ai/src/utils/idle-iterator.ts","lineNumber":349,"sourceCode":"\t\t\t\tif (firstItemDeadlineMs !== undefined) {\n\t\t\t\t\tactiveTimeoutMs = firstItemDeadlineMs - Date.now();\n\t\t\t\t\tif (activeTimeoutMs <= 0) {\n\t\t\t\t\t\tif (!hasPendingLocalWork()) {\n\t\t\t\t\t\t\toptions.onFirstItemTimeout?.();\n\t\t\t\t\t\t\tcloseIterator();\n\t\t\t\t\t\t\tthrow new AIError.StreamTimeoutError(options.firstItemErrorMessage ?? options.errorMessage);\n\t\t\t\t\t\t}\n\t\t\t\t\t\textendDeadlineForLocalWork();\n\t\t\t\t\t\tactiveTimeoutMs = firstItemDeadlineMs! - Date.now();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (options.idleTimeoutMs !== undefined && options.idleTimeoutMs > 0) {\n\t\t\t\tactiveTimeoutMs = options.idleTimeoutMs - (Date.now() - lastProgressAt);\n\t\t\t\tif (activeTimeoutMs <= 0) {\n\t\t\t\t\tif (!hasPendingLocalWork()) {\n\t\t\t\t\t\toptions.onIdle?.();\n\t\t\t\t\t\tcloseIterator();\n\t\t\t\t\t\tthrow new AIError.StreamTimeoutError(options.errorMessage);\n\t\t\t\t\t}\n\t\t\t\t\textendDeadlineForLocalWork();\n\t\t\t\t\tactiveTimeoutMs = options.idleTimeoutMs;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpendingNext ??= withRacy(iterator.next());\n\n\t\t\tconst racers: Array<\n\t\t\t\tPromise<\n\t\t\t\t\t| { kind: \"next\"; result: IteratorResult<T> }\n\t\t\t\t\t| { kind: \"error\"; error: unknown }\n\t\t\t\t\t| { kind: \"timeout\" }\n\t\t\t\t\t| { kind: \"abort\" }\n\t\t\t\t>\n\t\t\t> = [pendingNext];\n\n\t\t\tconst enforceTimeout = !noTimeoutEnforced && activeTimeoutMs !== undefined && activeTimeoutMs > 0;","sourceCodeStart":331,"sourceCodeEnd":367,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/utils/idle-iterator.ts#L331-L367","documentation":"AIError.StreamTimeoutError thrown by iterateWithIdleTimeout when no item has arrived within options.idleTimeoutMs of the last progress timestamp (or since start) and no local work is pending. Unlike the first-item variant, this is the general idle path: the stream started (or was expected to) but stalled. The message is exactly options.errorMessage.","triggerScenarios":"A wrapped stream (timedAnthropic/timedOpenai/codex SSE/etc.) goes silent longer than idleTimeoutMs between items — provider stopped sending chunks mid-generation, connection hung without RST, or an intermediary buffering indefinitely.","commonSituations":"Long tool-use/thinking phases where the provider legitimately sends nothing (idle timeout set too low); NAT or load balancer dropping an idle TCP connection; provider-side generation stall.","solutions":["Raise idleTimeoutMs to exceed the longest legitimate gap between stream events (e.g. extended thinking).","Handle the error with retry/resume logic — reconnect and continue if the provider supports resumable streams.","Use options.onIdle as a hook to log or send keep-alive before the throw.","Enable TCP/HTTP keep-alive at the fetch layer to survive NAT idle drops."],"exampleFix":"// before\niterateWithIdleTimeout(stream, { idleTimeoutMs: 5_000, errorMessage: \"stream timeout\" });\n// after\niterateWithIdleTimeout(stream, { idleTimeoutMs: 60_000, errorMessage: \"stream idle >60s, aborting\" });","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n\tfor await (const ev of timedStream) handle(ev);\n} catch (err) {\n\tif (err instanceof AIError.StreamTimeoutError) {\n\t\tlogger.warn(\"stream stalled past idleTimeoutMs — reconnecting\");\n\t\tawait reconnectAndResume();\n\t} else throw err;\n}","preventionTips":["Size idleTimeoutMs above the longest legitimate inter-chunk gap (thinking/tool phases).","Use onIdle for keep-alive or progress logging before abort.","Enable transport-level keep-alive to survive NAT/LB idle drops."],"tags":["timeout","streaming","network"],"backgroundTag":"stream-idle-timeout","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}