From 6f8384343a816ab31c22c12ecf31d373b95be7a4 Mon Sep 17 00:00:00 2001 From: WolfieLeader Date: Tue, 6 Oct 2026 16:33:56 +0300 Subject: [PATCH] fix(pg-cloudflare): do not emit an error for the read that end() cancels Since #3747, end() closes the Cloudflare socket while _listen() still waits on a read. workerd rejects that read with "This socket has been closed.", and pg re-emits it as a client 'error' after end() resolved. With no 'error' listener this is an unhandled rejection. Ignore a rejected read once end() has been called. Other read failures still emit 'error'. --- packages/pg-cloudflare/src/index.ts | 12 +++++++++++- packages/pg-esm-test/pg-cloudflare.test.js | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/pg-cloudflare/src/index.ts b/packages/pg-cloudflare/src/index.ts index 527b433f5..690177fcb 100644 --- a/packages/pg-cloudflare/src/index.ts +++ b/packages/pg-cloudflare/src/index.ts @@ -10,6 +10,7 @@ export class CloudflareSocket extends EventEmitter { private _upgrading = false private _upgraded = false + private _ending = false private _cfSocket: Socket | null = null private _cfWriter: WritableStreamDefaultWriter | null = null private _cfReader: ReadableStreamDefaultReader | null = null @@ -65,7 +66,15 @@ export class CloudflareSocket extends EventEmitter { // eslint-disable-next-line no-constant-condition while (true) { log('awaiting receive from CF socket') - const { done, value } = await this._cfReader!.read() + let result: ReadableStreamReadResult + try { + result = await this._cfReader!.read() + } catch (e) { + // closing the socket in end() rejects the read that is still pending + if (this._ending) break + throw e + } + const { done, value } = result log('CF socket received:', done, value) if (done) { log('done') @@ -110,6 +119,7 @@ export class CloudflareSocket extends EventEmitter { end(data = Buffer.alloc(0), encoding: BufferEncoding = 'utf8', callback: (...args: unknown[]) => void = () => {}) { log('ending CF socket') + this._ending = true this.write(data, encoding, (err) => { const socket = this._cfSocket const closePromise = socket?.close() diff --git a/packages/pg-esm-test/pg-cloudflare.test.js b/packages/pg-esm-test/pg-cloudflare.test.js index 683229158..6f9be18d9 100644 --- a/packages/pg-esm-test/pg-cloudflare.test.js +++ b/packages/pg-esm-test/pg-cloudflare.test.js @@ -55,4 +55,23 @@ describe('pg-cloudflare', () => { socket.end() }) }) + + it('should stop listening without an error when end() closes the socket', async () => { + const socket = new CloudflareSocket() + let rejectRead + socket._cfReader = { + read: () => + new Promise((resolve, reject) => { + rejectRead = reject + }), + } + socket._cfSocket = { + close: async () => rejectRead(new TypeError('This socket has been closed.')), + } + + const listening = socket._listen() + socket.end() + + await listening + }) })