can1357/oh-my-pi · warning · AIError.StreamTimeoutError
!awaitingFirstItem ? options.errorMessage : (options.firstIt
Error message
!awaitingFirstItem ? options.errorMessage : (options.firstItemErrorMessage ?? options.errorMessage)
What it means
AIError.StreamTimeoutError thrown from the deferred/racing path of iterateWithIdleTimeout (e.g. when the iterator was paused awaiting consumer work). It picks the message dynamically: options.errorMessage for a mid-stream idle timeout, or firstItemErrorMessage ?? errorMessage if the first item never arrived — mirroring the two synchronous timeout paths. This is the same contract as 626/627 surfaced through the async race branch.
Source
Thrown at packages/ai/src/utils/idle-iterator.ts:405
if (outcome.kind === "abort") {
closeIterator();
throw abortReason(abortSignal!);
}
if (outcome.kind === "timeout") {
if (hasPendingLocalWork()) {
// A local tool is still running; the provider cannot make
// progress until we hand its result back. Keep waiting.
extendDeadlineForLocalWork();
continuing = true;
continue;
}
if (!awaitingFirstItem) {
options.onIdle?.();
} else {
options.onFirstItemTimeout?.();
}
closeIterator();
throw new AIError.StreamTimeoutError(
!awaitingFirstItem ? options.errorMessage : (options.firstItemErrorMessage ?? options.errorMessage),
);
}
if (outcome.kind === "error") {
throw outcome.error;
}
if (outcome.result.done) {
markFirstItemReceived();
return;
}
const item = outcome.result.value;
// Non-progress items (e.g. provider keepalives, synthetic `start` events that
// arrive before the model has produced any tokens) MUST NOT flip us out of
// `awaitingFirstItem`. Otherwise the next iteration switches from the (longer)
// first-item watchdog to the (shorter) idle watchdog while we're still waiting
// on the model's first real output.
if (isProgressItem(item)) {
markFirstItemReceived();View on GitHub (pinned to 9690622007)
Solutions
- Tune both firstItemDeadlineMs and idleTimeoutMs so legitimate processing time between pulls doesn't trip the deadline.
- Provide distinct firstItemErrorMessage and errorMessage for diagnosability.
- Process chunks promptly or move heavy work off the pull path so deadlines reflect provider latency, not consumer slowness.
- Catch StreamTimeoutError and retry/fallback via onFirstItemTimeout/onIdle hooks.
Example fix
// before: heavy work between pulls trips the deadline
for await (const ev of timed) { await expensiveRender(ev); }
// after: decouple heavy work from the pull loop
for await (const ev of timed) { queue.push(ev); } // render off-loop Defensive patterns
Strategy: retry
Try / catch
try {
for await (const ev of timedStream) handle(ev);
} catch (err) {
if (err instanceof AIError.StreamTimeoutError) {
// same family as sync paths; inspect which deadline tripped via message
} else throw err;
} Prevention
- Keep per-chunk consumer work cheap; offload heavy processing off the pull loop.
- Provide both firstItemErrorMessage and errorMessage for triage.
- Treat this like 626/627: tune deadlines, add fallback hooks.
When it happens
Trigger: Same conditions as the first-item (626) and idle (627) timeouts, but detected by the internal race/timer path — typically when the consumer awaits slowly between pulls or the timer fires while the generator is suspended.
Common situations: Slow downstream consumer (heavy per-chunk processing) starving the iterator past the deadline; provider stall during a paused/suspended pull; identical root causes to 626/627.
Related errors
- options.firstItemErrorMessage ?? options.errorMessage
- options.errorMessage
- Timed out waiting for agent_end. Stderr: {self.stderr}
- timed out: {command}
- Request was aborted
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/51363598b4a0983e.
Report an issue: GitHub.