Skip to content

fix: prevent crash when aborting a request with a non-stream body - #1911

Closed
marceli1404 wants to merge 1 commit into
node-fetch:mainfrom
marceli1404:fix/abort-non-stream-body-crash
Closed

marceli1404 wants to merge 1 commit into
node-fetch:mainfrom
marceli1404:fix/abort-non-stream-body-crash

Conversation

@marceli1404

Copy link
Copy Markdown

Bug

Calling fetch() with a non-stream body (string, Buffer, Blob, URLSearchParams, etc.) and an already-aborted AbortController crashes the process:

const ac = new AbortController();
ac.abort();
fetch(url, { method: 'POST', body: 'hello world', signal: ac.signal });
// Unhandled 'error' event -> process terminates with exit code 1

The fetch promise rejects cleanly with AbortError, but the process still dies because the internally-created Stream.Readable.from(body) has no 'error' listener.

Root cause

In the Body constructor, the 'error' handler is attached only when the original body is a Stream:

if (body instanceof Stream) {
    body.on('error', ...);
}

For non-stream bodies, the actual stream used at runtime is Stream.Readable.from(...), which never gets a listener. When abort() calls request.body.destroy(error) on that un-listened stream, the error event is unhandled.

Fix

Attach the handler to stream (the value actually used) instead of body:

if (stream instanceof Stream) {
    stream.on('error', ...);
}

For stream bodies stream === body, so existing behavior is unchanged; for non-stream bodies the internally-created stream now gets the same handler.

Verification

  • New regression test fails on unpatched code (crash), passes with the fix.
  • Full AbortController describe block: 32 passing.
  • Targeted test passes across all three AbortController variants (native, mysticatea, polyfill).

@marceli1404

Copy link
Copy Markdown
Author

Note on overlap: this addresses the same root cause as the earlier open PR #1852 (\ ix crash on abort when body is not a stream) and issue #1801.

  • fix crash on abort when body is not a stream #1852 adds a parallel error listener only when stream !== body; this PR instead changes the existing condition from �ody instanceof Stream to stream instanceof Stream (single listener, same behavioral outcome) and includes a regression test that fails on unpatched code.
  • Whatever the maintainers prefer is fine with me: I'm happy to close this PR and port the regression test onto fix crash on abort when body is not a stream #1852, or keep this one as the canonical fix if the cleaner single-listener version is preferred.

This bug otherwise needs no further coordination on my side — both approaches fix the same crash.

@marceli1404 marceli1404 closed this by deleting the head repository Sep 2, 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.

1 participant