bug/proposal: infinitely hanging clients breaking bigger/complex sessions. Proposal: add Streaming Idle Timeout to Prevent Indefinite Hangs #867 #868
+679
−13
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This is an example PR for what's brought up in #867
I tried to ensure good test coverage.
Summary
This PR adds an
idleTimeoutoption to detect and abort streams that stop sending SSE events without closing the connection. This addresses a critical failure mode where streaming responses hang indefinitely when:The Problem
Currently, the SDK provides:
timeout: Overall request timeout (covers connection + response time)AbortController: Manual cancellation via signalsNeither detects stalled streams where the connection is alive but no data is flowing. When this happens,
for await (const event of stream)blocks forever with no error and no indication anything is wrong.Real-World Evidence
From production Claude Code CLI sessions, we observed:
"stop_reason": nullin logs - response never completedThe Solution
New
idleTimeoutoption that:Promise.raceto race each chunk read against a timeoutStreamIdleTimeoutErrorwith diagnostic information if the stream stallsChanges
src/client.tsidleTimeouttoClientOptionssrc/internal/request-options.tsidleTimeouttoRequestOptionssrc/core/error.tsStreamIdleTimeoutErrorclasssrc/core/streaming.ts_iterSSEMessagessrc/internal/parse.tsStream.fromSSEResponsesrc/index.tsStreamIdleTimeoutErrortests/streaming.test.tstests/api-resources/MessageStream.test.tsAPI
Type Safety
anytypes usedStreamIdleTimeoutErrorextendsAPIConnectionError(existing error hierarchy)idleTimeout?: number | undefinedto satisfyexactOptionalPropertyTypesTest Plan
npm run lintpassesnpm testpasses (427 tests total, including 17 new idle timeout tests)Test Coverage
Low-Level Unit Tests (
streaming.test.ts) - 13 testsidleTimeoutMs,eventCount,lastEventTimecontroller.abort()Client Integration Tests (
MessageStream.test.ts) - 4 testsclient.messages.stream(params, { idleTimeout })new Anthropic({ idleTimeout })Questions for Maintainers
idleTimeouthave a default (e.g., 2 minutes) or remain opt-in?pingSSE events reset the idle timer, or only "meaningful" events?maxRetries > 0?