Skip to content

fix(client): apply timeout to response body reads#1908

Open
redactdeveloper wants to merge 1 commit into
openai:mainfrom
redactdeveloper:fix/1825-body-read-timeout
Open

fix(client): apply timeout to response body reads#1908
redactdeveloper wants to merge 1 commit into
openai:mainfrom
redactdeveloper:fix/1825-body-read-timeout

Conversation

@redactdeveloper

Copy link
Copy Markdown

Summary

  • keep request timeouts active during stalled response body reads, not only until headers arrive
  • start the body timeout lazily when the response body is actually consumed, so asResponse() callers do not leave active timers behind
  • release the response body reader when the monitored stream closes, errors, or is cancelled
  • add regression coverage for a response that sends headers and then stalls before completing the body

Tests

  • ./scripts/test tests/index.test.ts
  • ./node_modules/.bin/jest tests/index.test.ts --runInBand --detectOpenHandles
  • ./node_modules/.bin/prettier --check src/client.ts tests/index.test.ts
  • ./node_modules/.bin/tsc --noEmit --skipLibCheck --target es2020 --lib es2020,dom,dom.iterable --module commonjs --moduleResolution node --esModuleInterop --strict --exactOptionalPropertyTypes --noUncheckedIndexedAccess --noImplicitOverride --noPropertyAccessFromIndexSignature src/client.ts

Fixes #1825

Previously the request timeout was cleared as soon as response headers arrived, leaving the body read (response.json(), streaming, etc.) unguarded. A server that sent headers and then stalled mid-body could hang the SDK indefinitely.

The abort timer now stays armed through the body read and is re-armed on each chunk, so a stalled body is aborted after the configured timeout of inactivity while a steadily-streaming response is never cut off. The timer is cleared once the body is fully read, cancelled, or errors.

Fixes openai#1825
@redactdeveloper redactdeveloper requested a review from a team as a code owner June 1, 2026 06:17

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2714d71129

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/client.ts
Comment on lines +1020 to +1021
rearmTimeout();
const { done, value } = await reader.read();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Route body timeouts through retry handling

When the server sends headers and then stalls the body, the new timer now fires from this pull() after makeRequest() has already returned a successful APIResponseProps, so the existing timeout classification/retry path around fetchWithAuth() is bypassed. With the default maxRetries, a stalled JSON or SSE body will surface as the raw abort/read error and will not be retried or normalized as APIConnectionTimeoutError, even though the client documents request timeouts as retryable.

Useful? React with 👍 / 👎.

Comment thread src/client.ts
// leave an active timer behind.
clearCurrentTimeout();

const reader = response.body.getReader();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Guard custom fetch bodies before calling getReader

In Node 20+ globalThis.ReadableStream exists, so any custom fetch that returns a node-fetch-style response with a Node Readable body reaches this path, but those bodies do not implement getReader(). Previously such responses could still be parsed via their own Response.text()/json() methods; now the request throws TypeError: response.body.getReader is not a function before returning, so custom fetch implementations with non-WHATWG bodies need to be left unwrapped or checked for getReader first.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SDK timeout option does not cover response-body read phase (can hang forever)

1 participant