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();