Skip to content

Abort requests with the signal passed to them - #112

Merged
MattCCC merged 5 commits into
masterfrom
abort-signal
Sep 27, 2026
Merged

MattCCC merged 5 commits into
masterfrom
abort-signal

Conversation

@MattCCC

@MattCCC MattCCC commented Sep 27, 2026

Copy link
Copy Markdown
Owner

This pull request fixes the signal setting, which had no effect: fetchf() replaced the signal passed to a request with the one it uses for timeouts and cancellation, so aborting it neither cancelled the request nor stopped its retries or polling.

🐛 Bug Fixes

  • Aborting the signal aborts the request: the abort is forwarded to the request, which fails with the reason of the abort like with fetch(). Aborting with controller.abort() cancels it like cancellable does, so its promise is only rejected with rejectCancelled: true, while a custom reason, e.g. the timeout of AbortSignal.timeout(), makes it fail with that reason.
  • Already aborted signals: a request whose signal is aborted already isn't sent at all.
  • Retries: aborted requests are no longer retried, whether they were aborted with the signal, by a newer request of a cancellable one or with abortRequest(). Before, a retry of a request cancelled by a newer one could cancel the newer one in turn.
  • Polling: polling stops as soon as the signal is aborted, also while it waits for the next attempt, so no timers are kept alive.
  • No leaks: requests stop listening to the signal once they are done, so one signal can be reused for many requests.
  • It works the same for createApiFetcher() endpoints, e.g. api.getReport({ signal }).
  • Documented the signal setting in the Request Cancellation section.

⚠️ Behavior Change

Aborted requests are not retried anymore, even when shouldRetry would retry them. Timeouts aren't affected, so they are still retried according to retryOn and shouldRetry.

🧪 Tests

  • Aborting a pending request, an already aborted signal, rejectCancelled, custom abort reasons, the timeout of the request, and that requests stop listening to the signal once they are done.
  • No retries after an abort, including one during the retry delay, and polling stopping when the signal is aborted while it waits or during a request.
  • createApiFetcher() endpoints, and delayInvocation() ending early when its signal is aborted.
  • Coverage stays at 100%.

📦 Bundle Size

The core bundles grow by about 0.1 KB (brotli). The size limits are raised to fit this and the other pull requests opened alongside it, so that they can be merged in any order.


Generated by Claude Code

fetchf() replaced the signal passed to a request with the one it uses for timeouts and cancellation, so aborting it had no effect. Its abort is now forwarded to the request, which isn't sent at all when the signal is aborted already, and fails with the reason of the abort like fetch() does. Aborted requests are no longer retried, and polling stops as soon as the signal is aborted, as its waits now end early.
Core bundles 7.75 KB -> 8.75 KB, dist/react/index.mjs 10.75 KB -> 11.75 KB and dist/react/index.js 11.75 KB -> 12.75 KB. The limits fit this and the other PRs opened alongside it (abort signal fix, ETag revalidation, retry jitter and methods, onDownloadProgress, responseType, focusThrottleInterval, initialData and typed OpenAPI endpoints), which add about 1 KB together, so that they can be merged in any order.
@github-actions

github-actions Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

size-limit report 📦

Path Size
dist/browser/index.mjs 7.42 KB (+1.45% 🔺)
dist/browser/index.global.js 7.58 KB (+1.32% 🔺)
dist/node/index.js 7.5 KB (+1.48% 🔺)
dist/react/index.mjs 10.42 KB (+0.84% 🔺)
dist/react/index.js 11.31 KB (+0.83% 🔺)

Comment thread src/request-handler.ts Resolved
…to the signal

The signal is known to be set inside the if block, so the optional chaining added a branch that can never run, which failed the 100% branch coverage threshold. AbortSignal has addEventListener wherever fetchff runs, e.g. in Node.js 18+ and browsers.
@MattCCC
MattCCC merged commit 5a6aae6 into master Sep 27, 2026
14 checks passed
@MattCCC
MattCCC deleted the abort-signal branch September 27, 2026 20:18
@MattCCC MattCCC mentioned this pull request Sep 27, 2026
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.

2 participants