Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose

function errored(err) {
stream && (stream.destroy(err), stream = null)
query && queryError(query, err)
query && (queryError(query, err), query = null)
initial && (queryError(initial, err), initial = null)
}

Expand Down Expand Up @@ -449,7 +449,14 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
if (initial)
return reconnect()

!hadError && (query || sent.length) && error(Errors.connection('CONNECTION_CLOSED', options, socket))
if (hadError) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition is slightly weird to me. If (we had an error or sent.length isn't 0) and query is empty then call error which will clean the query regardless right?

Copy link
Copy Markdown

@AceCodePt AceCodePt Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't your code just mean that regardless if we had an error or not we should just clear the queue (which error function does) and show econreset?

// Clear query when connection closes with error (e.g., ECONNRESET)
query && (queryError(query, Errors.connection('CONNECTION_CLOSED', options, socket)), query = null)
while (sent.length)
queryError(sent.shift(), Errors.connection('CONNECTION_CLOSED', options, socket))
} else {
(query || sent.length) && error(Errors.connection('CONNECTION_CLOSED', options, socket))
}
Comment on lines +452 to +459
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (hadError) {
// Clear query when connection closes with error (e.g., ECONNRESET)
query && (queryError(query, Errors.connection('CONNECTION_CLOSED', options, socket)), query = null)
while (sent.length)
queryError(sent.shift(), Errors.connection('CONNECTION_CLOSED', options, socket))
} else {
(query || sent.length) && error(Errors.connection('CONNECTION_CLOSED', options, socket))
}
(query || sent.length) && error(Errors.connection('CONNECTION_CLOSED', options, socket)) //and query=null somewhere

closedTime = performance.now()
hadError && options.shared.retries++
delay = (typeof backoff === 'function' ? backoff(options.shared.retries) : backoff) * 1000
Expand Down
39 changes: 39 additions & 0 deletions tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2637,4 +2637,43 @@ t('query during copy error', async() => {
'COPY_IN_PROGRESS', error.code,
await sql`drop table test`
]
})

t('end() completes after ECONNRESET error', { timeout: 5 }, async() => {
// Create a real connection, then terminate the backend to simulate ECONNRESET
const sql = postgres({ ...options, max: 1 })
const sql2 = postgres({ ...options, max: 1 })

// Get the connection's backend PID
const [{ pid }] = await sql`select pg_backend_pid() as pid`

// Start a query and immediately terminate the backend connection
const queryPromise = sql.unsafe('SELECT pg_sleep(1)').catch(e => e)

// Terminate the backend right after query starts using a different connection
await delay(10)
await sql2`select pg_terminate_backend(${ pid })`

// Wait for the query to fail with ECONNRESET
const error = await queryPromise

// Verify we got a connection error (57P01 = terminating connection, ECONNRESET, or CONNECTION_CLOSED)
if (!error || (error.code !== 'ECONNRESET' && error.code !== 'CONNECTION_CLOSED' && error.code !== '57P01' && !error.message.includes('terminated'))) {
await sql.end()
await sql2.end()
throw new Error('Expected connection error, got: ' + (error ? (error.code || error.message) : 'no error'))
}

// Now try to end the connection - this should complete, not hang
const endStart = Date.now()
await sql.end()
const endDuration = Date.now() - endStart

await sql2.end()

// end() should complete quickly (within 1 second), not hang forever
return [
true,
endDuration < 1000
]
})