Skip to content

fix: clean up abort listener after response lifecycle#1910

Open
meaqua9420 wants to merge 1 commit into
openai:mainfrom
meaqua9420:fix-fetch-timeout-signal-listener
Open

fix: clean up abort listener after response lifecycle#1910
meaqua9420 wants to merge 1 commit into
openai:mainfrom
meaqua9420:fix-fetch-timeout-signal-listener

Conversation

@meaqua9420

Copy link
Copy Markdown
  • I understand that this repository is auto-generated and my pull request may not be merged

Changes being requested

Fixes #1811.

This keeps the caller-provided AbortSignal listener long enough to preserve user abort behavior while the response body is still being consumed, then removes it once the response lifecycle has settled:

  • fetchWithTimeout() now returns the Response plus an idempotent cleanup callback for the caller-signal listener.
  • The SDK timeout is still cleared as soon as fetch resolves, preserving the current timeout behavior.
  • Normal JSON/text parsing removes the caller listener after body parsing settles.
  • Error-response body reads and retry/body-cancel paths run the same cleanup.
  • SSE streams pass cleanup into Stream.fromSSEResponse() so the listener is removed when iteration finishes or is closed/broken.

Difference from existing PRs

I re-checked the existing PRs for #1811 before opening this one.

Additional context & links

The motivating Deno behavior is that a listener attached to AbortSignal.timeout() can keep the timeout timer ref'd until it fires. Removing the listener after successful body/stream completion lets successful requests exit promptly while still preserving abort behavior during body consumption.

Tests

  • ./node_modules/.bin/jest tests/index.test.ts -t "caller abort listener|caller abort forwarding" --runInBand
  • ./node_modules/.bin/jest tests/index.test.ts --runInBand
  • ./node_modules/.bin/prettier --check src/client.ts src/core/streaming.ts src/internal/parse.ts tests/index.test.ts
  • ./node_modules/.bin/eslint src/client.ts src/core/streaming.ts src/internal/parse.ts tests/index.test.ts
  • git diff --check origin/master...HEAD
  • ./scripts/build

@meaqua9420 meaqua9420 requested a review from a team as a code owner June 1, 2026 17:29

@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: f49bf26187

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/internal/parse.ts
const text = await response.text();
return text as unknown as T;
})();
})().finally(() => cleanup?.());

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 Preserve abort forwarding for binary responses

For __binaryResponse endpoints such as file downloads and audio speech, this finally runs immediately after returning the raw Response, before the caller has consumed response.body with blob(), arrayBuffer(), or a reader. Because fetchWithTimeout() only passes the SDK controller signal to fetch, removing the caller-signal listener here means aborting the user-provided signal during the subsequent binary body read no longer aborts the underlying response body; cleanup needs to be deferred until the returned Response is consumed/cancelled (or handled separately for raw responses).

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in dd0e115. Binary/raw responses now return a Response whose body is wrapped with the same idempotent cleanup callback, so the caller abort listener is not removed when the raw Response is handed back. It stays active until the binary body is consumed, cancelled, or errors, preserving caller abort forwarding during text()/arrayBuffer()/reader consumption while still removing the listener afterward. Added regression coverage for successful binary body consumption and abort during binary body reading.

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.

fetchWithTimeout does not remove abort event listener on successful completion, preventing process exit on Deno

1 participant