Skip to main content

Error Handling

Errors reported by the server arrive as DatabaseError — a subclass of Error carrying PostgreSQL's structured error fields plus a few fields postgrejs adds itself.

DatabaseError fields​

FieldTypeDescription
severitystringe.g. ERROR, FATAL, PANIC.
codestringThe SQLSTATE code (e.g. 23505 for a unique violation, 57014 for a cancelled query).
detailstringSecondary, more detailed message.
hintstringA suggestion for fixing the problem.
positionnumberByte offset into the original query string where the error occurred, for a syntax error.
internalPositionstringPosition within an internally-generated query (e.g. from a function).
internalQuerystringThe internally-generated query text itself.
wherestringContext stack (which function/statement was executing) when the error occurred.
schemastringSchema name, for errors tied to a specific object.
tablestringTable name.
columnstringColumn name.
dataTypestringData type name.
constraintstringConstraint name, e.g. for a failed unique/foreign-key constraint.
lineNrnumber(postgrejs-added) 1-based line number within the submitted SQL text where the error occurred.
colNrnumber(postgrejs-added) 1-based column number on that line.
linestring(postgrejs-added) The literal source line the error points to.

lineNr/colNr/line are populated only for queries submitted through this library's query()/execute(): postgrejs takes the byte position Postgres reports and maps it back to a line/column in the SQL text you passed in, then appends a caret-pointer excerpt to the error message, so the error shows exactly where in your submitted SQL the failure occurred instead of only a raw byte offset.

import { DatabaseError } from 'postgrejs';

try {
await connection.query(`
select *
from customers
wher id = 1
`);
} catch (err) {
if (err instanceof DatabaseError) {
console.log(err.code); // "42601" (syntax_error)
console.log(err.lineNr); // 4
console.log(err.colNr); // 5
console.log(err.line); // " wher id = 1"
}
}

Server Notices​

PostgreSQL sends some messages that aren't errors — a RAISE NOTICE, or its own "table does not exist, skipping" from DROP TABLE IF EXISTS. These never reject the running call or change its result; the statement completes normally. They arrive as 'notice' events, shaped like a DatabaseError (same severity/message/code fields) but never thrown:

connection.on('notice', msg => console.log(msg.severity, msg.message));
await connection.query('drop table if exists no_such_table');
// logs: NOTICE table "no_such_table" does not exist, skipping
// the query above still resolves normally

On a Pool, listen with pool.on('notice', (msg, connection) => ...) — a pool.query() caller never holds the Connection a notice came from, so it's named the same way 'destroy' names one for a lost connection. See Connection's 'notice' event and Pool's for the full reference.

Lost Connections​

A connection's socket can close on its own — the backend killed by an administrator, a failover, a network fault — as opposed to a close() the application itself called. When that happens:

  • Whatever call was running on the connection at the time rejects with a ConnectionLostError instead of a DatabaseError, since the server never got to answer.
  • Connection's 'error' event fires with that same error object — the one place code ported from pg listens, and the only report there is for a connection that was sitting idle with nothing to reject.
  • Connection's 'close' event also fires, with the same object as its reason.
  • On a Pool, the same object is the reason on 'destroy' and is emitted on the pool's own 'error' — with no second argument, which is what separates it from the pool's other use of 'error' (failing to create a connection).

ConnectionLostError.code is always '08006' (connection_failure), a value the server itself never sends, so branching on code reliably tells a lost connection apart from a DatabaseError without an instanceof check, which fails across duplicated copies of the package:

try {
await connection.query('select pg_sleep(30)');
} catch (err) {
if (err.code === '08006') {
console.warn('connection lost mid-query, pid was', err.processID);
} else if (err instanceof DatabaseError) {
console.error('database error:', err.code);
}
}

A close() you called still rejects an in-flight call with a plain Error('Connection closed'); that case carries no code, since nothing was actually lost.

asyncErrorHandling​

By default (asyncErrorHandling: true), postgrejs captures the caller's stack at the moment query()/execute() is called and splices it onto any error the call rejects with. Without this, an error thrown deep inside the library's protocol-handling code would only carry an internal async stack frame — useful for debugging the driver itself, but useless for finding which line of your application issued the failing query.

This costs a measurable amount of CPU when many calls are in flight at once (a burst of pipelined queries on one connection). For benchmarking, or in hot paths where the extra stack capture isn't worth it, it can be disabled globally via DatabaseConnectionParams.asyncErrorHandling, or per call via the same option on QueryOptions/ScriptExecuteOptions:

const connection = new Connection({
host: 'localhost',
user: 'app',
password: 'secret',
database: 'app_db',
asyncErrorHandling: false, // default: true
});

// or per call
await connection.query('select 1', { asyncErrorHandling: false });

See also​