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.aborted
req.complete
req.signal.aborted
req.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.close fires before signal.aborted becomes 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.close triggers
- later,
req.signal.aborted becomes true
req.aborted remains false
Key evidence
req.close event in route handler is logged before the route observes signal.aborted === true
req.aborted remains false
- 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.
Version
v24.18.0
Platform
Subsystem
No response
What steps will reproduce the bug?
In Node 24.18.0,
IncomingMessage.prototype.signal.abortedbecomes true after the incoming message body has finished parsing and the request stream closes, even thoughreq.aborted === false. This makesreq.signal.abortedunreliable for detecting actual client aborts.Context
Suggested reproduction steps
req.abortedreq.completereq.signal.abortedreq.socket.destroyedsignalAborted: falsesignal.aborted: truereq.closefires beforesignal.abortedbecomes trueCode to reproduce:
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.abortedshould only becometruewhen 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:
signalAborted: falsereq.closetriggersreq.signal.abortedbecomes truereq.abortedremains falseKey evidence
req.closeevent in route handler is logged before the route observessignal.aborted === truereq.abortedremainsfalseThis 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.signalAPI 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 ofreq.signal.abortedfor client abort detection.