From 2b92d335c22d604308d1a673cb0f6f4a652746d2 Mon Sep 17 00:00:00 2001 From: HirenGajjar Date: Wed, 13 May 2026 02:44:51 -0700 Subject: [PATCH] fix(response): make send() a no-op when response already finished Fixes #134 UwsResponse.send() threw 'Response already sent' when called on an already-finished response. This occurred during NestJS's normal exception-filter flow: when a handler sends a response then throws, NestJS calls adapter.end(response) for graceful cleanup, which delegated to send() and triggered the throw. The throw bubbled into RouteRegistry.handleException() and was logged as a spurious 'Unhandled route error' stack trace, even though the client had already received their response successfully. Changed the guard from a throw to a silent return, matching the behavior of Express and Fastify. Updated existing unit test in response.spec.ts to assert the new no-op behavior (verifies end() is called exactly once, not twice). --- src/http/core/response.spec.ts | 5 +++-- src/http/core/response.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/http/core/response.spec.ts b/src/http/core/response.spec.ts index 95df507..5338ffe 100644 --- a/src/http/core/response.spec.ts +++ b/src/http/core/response.spec.ts @@ -536,9 +536,10 @@ describe('UwsResponse', () => { expect(mockUwsRes.writeHeader).toHaveBeenCalledWith('set-cookie', 'user=vikram'); }); - it('should throw if already sent', () => { + it('should be a no-op if already sent', () => { res.send('First'); - expect(() => res.send('Second')).toThrow('Response already sent'); + expect(() => res.send('Second')).not.toThrow(); + expect(mockUwsRes.end).toHaveBeenCalledTimes(1); }); it('should not throw if aborted', () => { diff --git a/src/http/core/response.ts b/src/http/core/response.ts index 90fa644..54d50bf 100644 --- a/src/http/core/response.ts +++ b/src/http/core/response.ts @@ -1268,7 +1268,7 @@ export class UwsResponse extends Writable { } if (this.finished) { - throw new Error('Response already sent'); + return; } if (this.sending) {