Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

pg-native: unhandled libpq result status drops the result and the server's error

Aperta
#3,758 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
58/100
Tipo di issue
Bug
Chiarezza
Abbastanza chiara
Stato di attività
Attiva
Stack tecnologico
javascript, node.js, postgresql
Ambito
backend, database

Direzione di ricerca

Inizia riproducendo il caso fornito COPY … TO STDOUT con i client native e pure-JS. Leggi packages/pg-native/index.js intorno a _emitResult, _read e _onReadyForQuery, quindi packages/pg/lib/native/query.js intorno a after(). Il lavoro è completato quando uno stato libpq non gestito non produce più un risultato riuscito undefined e l’errore del server raggiunge il callback della query.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Disclaimer

This is a real issue we saw in our production environment via sequelize v6, we used claude + 2 agents to generate this report, I saw no AI/slop policy, feel free to do whatever you feel like based on this.

Summary

client.query(sql, cb) on the native client can call cb(null, undefined) — no error, no result — so anything that reads res.rows throws. The pure-JS client returns a Result for the same query.

Versions: pg 8.23.0, pg-native 3.9.0, libpq 1.11.0, Node 26.7.0. Reproduced on PostgreSQL 17.11 and CockroachDB 26.2.5 with identical output. Line numbers below are from master; the published 3.9.0 tarball is offset by ~17 lines around _onReadyForQuery.

Repro

COPY … TO STDOUT through client.query() hits it every time, no patching needed. This runs the same query through both drivers:

const CONN = process.argv[2] || 'postgres://postgres@localhost:5432/postgres';
const SQL = 'COPY (SELECT 1) TO STDOUT';

const native = require('pg').native;
const { Client: JsClient } = require('pg');

function run(Client, label) {
  return new Promise((resolve) => {
    const client = new Client(CONN);
    client.connect((connErr) => {
      if (connErr) return resolve(console.log(`[${label}] connect failed: ${connErr.message}`));
      client.query(SQL, (err, res) => {
        console.log(`[${label}] err = ${err && err.message}`);
        console.log(`[${label}] res = ${res && res.constructor.name}`);
        try {
          console.log(`[${label}] res.rows = ${JSON.stringify(res.rows)}`);
        } catch (e) {
          console.log(`[${label}] res.rows THREW ${e.constructor.name}: ${e.message}`);
        }
        client.end(() => resolve());
      });
    });
  });
}

(async () => {
  await run(JsClient, 'js    ');
  await run(native.Client, 'native');
})();
[js    ] err = null
[js    ] res = Result
[js    ] res.rows = []
[native] err = null
[native] res = undefined
[native] res.rows THREW TypeError: Cannot read properties of undefined (reading 'rows')

What's happening

_emitResult (packages/pg-native/index.js:181) emits a result event for three statuses only — PGRES_TUPLES_OK, PGRES_COMMAND_OK, PGRES_EMPTY_QUERY. COPY_OUT/COPY_BOTH break out without one, and the default: branch sends the error to the client through _readError rather than to the query. Either way _read reaches emit('readyForQuery') (:248) with _resultCount === 0, and _onReadyForQuery (:490) passes that straight through:

const results = this._results   // undefined: no 'result' event fired
cb(err, rows || [], results)

pg's NativeQuery then reads the falsy err as success and calls back with undefined (packages/pg/lib/native/query.js:121).

The case that actually bit us

COPY is just the convenient reproduction. The default: branch is the damaging one, because the server's error goes to the client's error event and never reaches the query — so you lose the result and the explanation. We spent a while on a Sequelize crash that looked like this:

TypeError: Cannot read properties of undefined (reading 'rows')
    at Query.run (sequelize/lib/dialects/postgres/query.js:62)

The transaction was left aborted, every later statement failed with current transaction is aborted, commands ignored until end of transaction block, and the error explaining why was gone. On the JS driver the same server condition arrives as an ordinary error.

Possible fixes

  • In _onReadyForQuery, when there's no result and no _queryError, call back with an error instead of undefined.
  • In the default: branch, set _queryError so the server's message reaches the caller.
  • Or defensively in pg's after() (packages/pg/lib/native/query.js:121): treat a missing results as an error.

Happy to open a PR if one of those looks like the right shape.


Possibly related, and I can file it separately: at packages/pg-native/index.js:214, if pq.consumeInput() returns false, _read calls _readError() and returns before emit('readyForQuery') — the in-flight query's callback never fires and the promise hangs. I saw this by cancelling a running statement server-side (on CockroachDB; haven't rechecked on PostgreSQL).

Lingua principale
JavaScript
Stelle
13.2k
Fork
1.4k
Merge medio
6g 15h
PR unite (30g)
6

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di brianc/node-postgres

Tutte le issue di brianc/node-postgres

Issue simili

Altre issue su JavaScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.