Skip to content

fix(websockets): send WsException errors to native WebSocket clients#16366

Open
veeceey wants to merge 4 commits intonestjs:v12.0.0from
veeceey:fix/issue-9056-ws-exception-filter
Open

fix(websockets): send WsException errors to native WebSocket clients#16366
veeceey wants to merge 4 commits intonestjs:v12.0.0from
veeceey:fix/issue-9056-ws-exception-filter

Conversation

@veeceey
Copy link

@veeceey veeceey commented Feb 15, 2026

Summary

BaseWsExceptionFilter called client.emit('exception', payload) to send errors, but native WebSocket clients (via @nestjs/platform-ws) don't have an emit method — only Socket.IO clients do. This meant that when using WsAdapter, any WsException thrown in a @SubscribeMessage handler was silently swallowed and never reached the client.

On top of that, WsExceptionsHandler.handle() had a !client.emit guard that short-circuited the entire exception handling pipeline for non-Socket.IO clients.

Changes

  • Added a protected emitMessage() method to BaseWsExceptionFilter that checks whether the client has emit (Socket.IO) or send (native WS) and dispatches accordingly
  • For native WS clients, errors are sent as JSON.stringify({ event: 'exception', data: payload }), consistent with the { event, data } message format the WS adapter already uses
  • Changed the !client.emit guard in WsExceptionsHandler to !client, so exceptions are properly handled regardless of adapter type
  • Updated type constraints from { emit: Function } to { emit?: Function; send?: Function } to accept both client types

Test plan

  • Added unit tests for native WS client error handling (unknown errors, WsException with object, WsException with string, includeCause=false)
  • Added e2e integration test (ws-error-gateway.spec.ts) that spins up a WsAdapter-based gateway, sends a message that triggers a WsException, and asserts the error response is received by the client
  • Existing Socket.IO tests remain unchanged and should continue to pass

Closes #9056

@coveralls
Copy link

coveralls commented Feb 15, 2026

Pull Request Test Coverage Report for Build e10f6819-b1b4-4f6a-81e5-857fe08aabe0

Details

  • 10 of 10 (100.0%) changed or added relevant lines in 2 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.006%) to 89.835%

Totals Coverage Status
Change from base Build 5b92b95b-28e1-48d1-abca-174661980de8: 0.006%
Covered Lines: 7468
Relevant Lines: 8313

💛 - Coveralls

@kamilmysliwiec
Copy link
Member

Could you rebase to v12.0.0?

@veeceey
Copy link
Author

veeceey commented Feb 19, 2026

@kamilmysliwiec Absolutely, I'll rebase onto v12.0.0 and push the updated branch. Will get that done shortly!

@veeceey veeceey force-pushed the fix/issue-9056-ws-exception-filter branch from f760dae to 35a2306 Compare February 19, 2026 04:59
@veeceey veeceey changed the base branch from master to v12.0.0 February 19, 2026 04:59
@kamilmysliwiec
Copy link
Member

image

@veeceey
Copy link
Author

veeceey commented Feb 20, 2026

Thanks for flagging that @kamilmysliwiec — looks like the build is failing after the rebase. I'll look into the CI failure and push a fix. Apologies for the breakage.

@veeceey
Copy link
Author

veeceey commented Feb 23, 2026

All good now - CI is passing. The build-and-test job completed successfully.

The BaseWsExceptionFilter used client.emit() which only works with
Socket.IO clients. Native WebSocket clients (via @nestjs/platform-ws)
don't have an emit method, so exceptions were silently swallowed.

Added an emitMessage() method that detects the client type and uses
client.send() with JSON-serialized { event, data } payloads for native
WS clients, matching the message format the WS adapter already uses.
Also removed the !client.emit guard in WsExceptionsHandler that
prevented exception handling from reaching native WS clients at all.

Closes nestjs#9056
The emitMessage() method checked for client.emit first, but native
WebSocket clients from the ws package inherit from EventEmitter and
also have an emit method that only dispatches events locally. This
caused WsException errors to be emitted as local events instead of
being sent over the wire, resulting in the e2e test timing out.

Added an isNativeWebSocket() check that looks for a numeric readyState
property (per the WebSocket spec) to reliably distinguish native WS
clients from Socket.IO sockets. Also fixed prettier formatting on the
emitMessage generic signature, and updated test mocks to include
readyState on the native WS client stub.
Replace sinon.stub() and Chai-style assertions with Vitest's vi.fn()
and expect() to fix CI failures in the test environment.
The ws-error-gateway integration test was still using chai assertions
and CJS-style imports which are incompatible with the v12.0.0 branch
that has migrated to Vitest and ESM. Replaced chai's expect with
Vitest globals, changed to default WebSocket import, added .js
extension to local import, and removed a stray console.log from the
unit test.
@veeceey veeceey force-pushed the fix/issue-9056-ws-exception-filter branch from 8cd7c66 to 104ca52 Compare February 24, 2026 07:31
@veeceey veeceey force-pushed the fix/issue-9056-ws-exception-filter branch from 104ca52 to 4ee5330 Compare March 12, 2026 04:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Not throwing the exception/error when using ws adapter websocket

3 participants