From 3fe53ea067b5d395fd469df0991c0884fad786d9 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 8 Apr 2026 08:42:45 +0200 Subject: [PATCH] websocket: pass failure cause through to error event The cause parameter in failWebsocketConnection was not being passed through to onSocketClose, resulting in the TypeError fired in the error event missing its cause property (e.g., the ECONNREFUSED error). Pass the cause as an argument through onSocketClose to #onSocketClose so that the TypeError is created with { cause } when available. Fixes: https://github.com/nodejs/undici/issues/4991 --- lib/web/websocket/connection.js | 2 +- lib/web/websocket/websocket.js | 6 +++--- test/websocket/issue-4273.js | 6 ++++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/web/websocket/connection.js b/lib/web/websocket/connection.js index 4ecc8a195fc..e9f2c62ebb5 100644 --- a/lib/web/websocket/connection.js +++ b/lib/web/websocket/connection.js @@ -316,7 +316,7 @@ function failWebsocketConnection (handler, code, reason, cause) { if (isConnecting(handler.readyState)) { // If the connection was not established, we must still emit an 'error' and 'close' events - handler.onSocketClose() + handler.onSocketClose(cause) } else if (handler.socket?.destroyed === false) { handler.socket.destroy() } diff --git a/lib/web/websocket/websocket.js b/lib/web/websocket/websocket.js index da94ab5b352..9ce88874955 100644 --- a/lib/web/websocket/websocket.js +++ b/lib/web/websocket/websocket.js @@ -92,7 +92,7 @@ class WebSocket extends EventTarget { this.#handler.socket.destroy() }, - onSocketClose: () => this.#onSocketClose(), + onSocketClose: (cause) => this.#onSocketClose(cause), onPing: (body) => { if (channels.ping.hasSubscribers) { channels.ping.publish({ @@ -565,7 +565,7 @@ class WebSocket extends EventTarget { * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol * @see https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.4 */ - #onSocketClose () { + #onSocketClose (cause) { // If the TCP connection was closed after the // WebSocket closing handshake was completed, the WebSocket connection // is said to have been closed _cleanly_. @@ -599,7 +599,7 @@ class WebSocket extends EventTarget { code = 1006 fireEvent('error', this, (type, init) => new ErrorEvent(type, init), { - error: new TypeError(reason) + error: new TypeError(reason, cause ? { cause } : undefined) }) } diff --git a/test/websocket/issue-4273.js b/test/websocket/issue-4273.js index d41e35665c3..b82860f48b1 100644 --- a/test/websocket/issue-4273.js +++ b/test/websocket/issue-4273.js @@ -5,12 +5,14 @@ const { WebSocket } = require('../..') const { once } = require('node:events') test('first error than close event is fired on failed connection', async (t) => { - t.plan(1) + t.plan(3) - const ws = new WebSocket('ws://localhost:1') + const ws = new WebSocket('ws://localhost:999') ws.addEventListener('error', (ev) => { t.assert.ok(ev.error instanceof TypeError) + t.assert.ok(ev.error.cause, 'error should have a cause') + t.assert.strictEqual(ev.error.cause.code, 'ECONNREFUSED', 'error should have a cause code of ECONNREFUSED') }) await once(ws, 'close')