Commit 8314231539d for nodejs
commit 8314231539dfc9d12ec17777ffd8735f90db52de
Author: Christian Aurich Zanettini Martins <christian.aurichzm@gmail.com>
Date: Tue Oct 6 14:17:15 2026 -0300
quic: destroy stream on invalid promise body
When `setBody()` receives a promise that resolves to an unsupported
body type, `configureOutbound()` throws inside the fulfillment
handler. The rejection handler only covered the original promise, so
the error rejected a discarded promise and crashed the process as an
unhandled rejection.
Catch errors thrown by `configureOutbound()` in the fulfillment
handler and destroy the stream with them, as the rejection handler
already does. For an unsupported resolved value, `stream.closed` now
rejects with the same `ERR_INVALID_ARG_TYPE` that `setBody()` throws
synchronously.
Signed-off-by: Christian Aurich Zanettini Martins <christian.aurichzm@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66260
Reviewed-By: Tim Perry <pimterry@gmail.com>
diff --git a/lib/internal/quic/quic.js b/lib/internal/quic/quic.js
index 4922ce56275..7809a32665b 100644
--- a/lib/internal/quic/quic.js
+++ b/lib/internal/quic/quic.js
@@ -1375,14 +1375,21 @@ function configureOutbound(handle, stream, body) {
// Handle Promise - await and recurse. Native promises auto-flatten,
// so the resolved value will never itself be a promise.
if (isPromise(body)) {
+ const onError = (err) => {
+ if (!stream.destroyed) {
+ stream.destroy(err);
+ }
+ };
PromisePrototypeThen(
body,
- (resolved) => configureOutbound(handle, stream, resolved),
- (err) => {
- if (!stream.destroyed) {
- stream.destroy(err);
+ (resolved) => {
+ try {
+ configureOutbound(handle, stream, resolved);
+ } catch (err) {
+ onError(err);
}
},
+ onError,
);
return;
}
diff --git a/test/parallel/test-quic-stream-body-promise-invalid.mjs b/test/parallel/test-quic-stream-body-promise-invalid.mjs
new file mode 100644
index 00000000000..65f4dd27bb3
--- /dev/null
+++ b/test/parallel/test-quic-stream-body-promise-invalid.mjs
@@ -0,0 +1,39 @@
+// Flags: --experimental-quic --no-warnings
+
+// Test: body: Promise resolving to an invalid body destroys the stream.
+// When the body is a Promise that resolves to an unsupported type, the
+// stream should be destroyed with the same ERR_INVALID_ARG_TYPE that
+// setBody() throws synchronously for that type.
+
+import { hasQuic, skip, mustCall } from '../common/index.mjs';
+import assert from 'node:assert';
+
+if (!hasQuic) {
+ skip('QUIC is not enabled');
+}
+
+const { listen, connect } = await import('../common/quic.mjs');
+
+const serverEndpoint = await listen(mustCall(async (serverSession) => {
+ await serverSession.closed;
+}), { transportParams: { maxIdleTimeout: 5 } });
+
+const clientSession = await connect(serverEndpoint.address, {
+ transportParams: { maxIdleTimeout: 5 },
+});
+await clientSession.opened;
+
+const stream = await clientSession.createBidirectionalStream();
+
+assert.throws(() => stream.setBody(42), { code: 'ERR_INVALID_ARG_TYPE' });
+
+const stream2 = await clientSession.createBidirectionalStream();
+
+const closedPromise = assert.rejects(stream2.closed, {
+ code: 'ERR_INVALID_ARG_TYPE',
+});
+
+stream2.setBody(Promise.resolve(42));
+
+await Promise.all([closedPromise, clientSession.closed]);
+await serverEndpoint.close();