From d03fb6cc917ea8c35703b3067a9ccf3f46456aba Mon Sep 17 00:00:00 2001 From: Damola Adegbite <101389494+dax-side@users.noreply.github.com> Date: Tue, 3 Nov 2026 21:23:25 +0000 Subject: [PATCH] fix(pg-cloudflare): emit an error when a write without a callback fails pg's Connection calls stream.write(buffer) without a callback. When the Cloudflare writer rejected, the error went to a default no-op callback and was lost; the application only saw a later "Connection terminated unexpectedly". Now, when no callback is given, the rejection is emitted as an 'error' event, as a Node net.Socket would do. A caller that passes a callback still gets the error there, as before. Part of #3757 (the error-reporting half; chunking large writes is not changed here). --- packages/pg-cloudflare/src/index.ts | 10 ++++--- packages/pg-esm-test/pg-cloudflare.test.js | 31 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/packages/pg-cloudflare/src/index.ts b/packages/pg-cloudflare/src/index.ts index 527b433f5..d675399d6 100644 --- a/packages/pg-cloudflare/src/index.ts +++ b/packages/pg-cloudflare/src/index.ts @@ -87,22 +87,24 @@ export class CloudflareSocket extends EventEmitter { write( data: Uint8Array | string, encodingOrCallback: BufferEncoding | ((error?: unknown) => void) = 'utf8', - callback: (error?: unknown) => void = () => {} + callback?: (error?: unknown) => void ): true | void { const encoding = typeof encodingOrCallback === 'function' ? 'utf8' : encodingOrCallback if (typeof encodingOrCallback === 'function') callback = encodingOrCallback - if (data.length === 0) return callback() + if (data.length === 0) return callback?.() if (typeof data === 'string') data = Buffer.from(data, encoding) log('sending data direct:', data) this._cfWriter!.write(data).then( () => { log('data sent') - callback() + callback?.() }, (err) => { log('send error', err) - callback(err) + // pg calls write() without a callback, so emit the error the way a net.Socket would + if (callback) callback(err) + else this.emit('error', err) } ) return true diff --git a/packages/pg-esm-test/pg-cloudflare.test.js b/packages/pg-esm-test/pg-cloudflare.test.js index 683229158..4a10d80b5 100644 --- a/packages/pg-esm-test/pg-cloudflare.test.js +++ b/packages/pg-esm-test/pg-cloudflare.test.js @@ -38,6 +38,37 @@ describe('pg-cloudflare', () => { await promise }) + it('should emit an error when a write without a callback fails', async () => { + const socket = new CloudflareSocket() + const writeError = new Error('write failed') + socket._cfWriter = { write: () => Promise.reject(writeError) } + + const error = await new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error('error event was not emitted')), 100) + socket.once('error', (err) => { + clearTimeout(timer) + resolve(err) + }) + socket.write(Buffer.from('x')) + }) + assert.equal(error, writeError) + }) + + it('should pass a failed write to the callback instead of emitting an error', async () => { + const socket = new CloudflareSocket() + const writeError = new Error('write failed') + socket._cfWriter = { write: () => Promise.reject(writeError) } + let emitted = false + socket.on('error', () => { + emitted = true + }) + + const error = await new Promise((resolve) => socket.write(Buffer.from('x'), resolve)) + assert.equal(error, writeError) + await new Promise((resolve) => setTimeout(resolve, 10)) + assert.equal(emitted, false) + }) + it('should emit close when ending a socket whose closed promise never settles', async () => { const socket = new CloudflareSocket() socket._cfSocket = {