Repository navigation
req.signal.aborted becomes true after request body parse / stream close in Node 24.18.0 #64390
Description
Version
v24.18.0
Platform
Darwin Deniss-MacBook-Pro.local 25.5.0 Darwin Kernel Version 25.5.0: Mon Apr 27 20:41:19 PDT 2026; root:xnu-12377.121.6~2/RELEASE_ARM64_T8122 arm64
Subsystem
No response
What steps will reproduce the bug?
In Node 24.18.0, IncomingMessage.prototype.signal.aborted becomes true after the incoming message body has finished parsing and the request stream closes, even though req.aborted === false. This makes req.signal.aborted unreliable for detecting actual client aborts.
Context
- Node version: v24.18.0
- Environment: local dev, no reverse proxy
- Framework: Express + body-parser
- Route: PATCH /tables/1
- Comparison: a separate GET route does not show the issue
Suggested reproduction steps
- Start an Express server on Node 24.18.0
- Add middleware logging:
req.abortedreq.completereq.signal.abortedreq.socket.destroyed
- Send a PATCH request with a JSON body
- Observe:
- early middleware logs
signalAborted: false - later the same request logs
signal.aborted: true req.closefires beforesignal.abortedbecomes true
Code to reproduce:
// repro.js
import express from "express";
import bodyParser from "body-parser";
const app = express();
app.use(bodyParser.json());
app.use((req, res, next) => {
console.log("early middleware:", {
url: req.originalUrl,
method: req.method,
aborted: req.aborted,
complete: req.complete,
signalAborted: req.signal.aborted
});
req.once("close", () => {
console.log("req close event", req.originalUrl, {
aborted: req.aborted,
signalAborted: req.signal.aborted
});
});
next();
});
app.patch("/tables/1", async (req, res) => {
console.log("route start:", {
aborted: req.aborted,
signalAborted: req.signal.aborted
});
await new Promise((resolve) => setTimeout(resolve, 200));
console.log("route before res send", {
aborted: req.aborted,
signalAborted: req.signal.aborted
});
res.json({ ok: true });
});
app.listen(3009, () => {
console.log("listening on 3009");
});
How often does it reproduce? Is there a required condition?
Reproduces with PATCH requests that have a JSON body.
What is the expected behavior? Why is that the expected behavior?
req.signal.aborted should only become true when the client aborts the request (transport-level cancellation before or during request processing), not when the incoming message handling completes normally or when body parsing ends.
What do you see instead?
For a normal PATCH request with a body:
- early request logs show
signalAborted: false - request body parsing completes successfully
req.closetriggers- later,
req.signal.abortedbecomes true req.abortedremains false
Key evidence
req.closeevent in route handler is logged before the route observessignal.aborted === truereq.abortedremainsfalse- normal cURL request reproduces it
- the same request type in a different route does not reproduce it
This means the new signal.aborted property is firing on request stream completion/closure rather than on genuine abort.
Additional information
Why this is a bug
The new req.signal API is supposed to expose request cancellation state. If it becomes true after normal request completion or stream close, it is effectively useless for distinguishing real client aborts. This appears to be the Node HTTP internals abort signal being fired when the request handling completes, rather than when the client actually aborts. That behavior breaks the use case of req.signal.aborted for client abort detection.
Activity
OP-Prajwal commentedon Jul 9, 2026
I reproduced this on Node.js v24.18.0 using Express.
For a normal PATCH request with a JSON body, I got:
--- route started --- initial: { reqAborted: false, signalAborted: false } EVENT: req.signal abort { reqAborted: false, signalAborted: true } EVENT: req close { reqAborted: false, signalAborted: true } before response: { reqAborted: false, signalAborted: true }I also tested a normal GET request:
--- GET route started --- GET before response: { reqAborted: false, signalAborted: false } GET close: { reqAborted: false, signalAborted: false } GET signal abort: { reqAborted: false, signalAborted: true }In both tests,
req.abortedremainedfalse, so the client did not abort the request.The GET result suggests this is not limited to JSON body parsing or
PATCH:req.signalis also aborted after a normal GET request completes. Could someone confirm whether this is the intended lifecycle behavior ofIncomingMessage.signal?If this is a bug or a documentation gap, I would be happy to contribute a regression test or documentation update. Please let me know what would be most helpful.
Archkon commentedon Jul 9, 2026
dnorrstrand commentedon Jul 21, 2026
Hi, I’m following up because this bug also affects other users and has significant impact on the reliability of request-abort handling. Could you please share whether there is an expected timeline for the fix and when a new Node.js release containing the resolution might be available? Thanks.
Archkon commentedon Jul 21, 2026
http: avoid aborting IncomingMessage signal on normal close
http: avoid aborting IncomingMessage signal on normal close
http: avoid aborting IncomingMessage signal on normal close
http: avoid aborting IncomingMessage signal on normal close
http: avoid aborting IncomingMessage signal on normal close
http: avoid aborting IncomingMessage signal on normal close