jackc/pgx · error
no more results in batch
Error message
no more results in batch
What it means
Returned by (*batchResults).Exec when the underlying MultiResultReader reports no further result sets. pgx asked PostgreSQL for the next result of a pipelined batch and got ReadyForQuery instead, so the read index advanced past the last executed query. The message means the client consumed more result slots than it queued (or a server-side error aborted the remaining statements, suppressing their result sets).
Source
Thrown at batch.go:143
closed bool
endTraced bool
}
// Exec reads the results from the next query in the batch as if the query has been sent with Exec.
func (br *batchResults) Exec() (pgconn.CommandTag, error) {
if br.err != nil {
return pgconn.CommandTag{}, br.err
}
if br.closed {
return pgconn.CommandTag{}, fmt.Errorf("batch already closed")
}
query, arguments, _ := br.nextQueryAndArgs()
if !br.mrr.NextResult() {
err := br.mrr.Close()
if err == nil {
err = errors.New("no more results in batch")
}
if br.conn.batchTracer != nil {
br.conn.batchTracer.TraceBatchQuery(br.ctx, br.conn, TraceBatchQueryData{
SQL: query,
Args: arguments,
Err: err,
})
}
return pgconn.CommandTag{}, err
}
commandTag, err := br.mrr.ResultReader().Close()
if err != nil {
br.err = err
br.mrr.Close()
}
if br.conn.batchTracer != nil {View on GitHub (pinned to ec1a0befd2)
Solutions
- Drive results through callbacks via QueuedQuery.Exec/Query/QueryRow and rely on BatchResults.Close, instead of manually calling Exec() N times.
- Make sure the number of result reads (Exec/Query/QueryRow) equals Batch.Len(); track the count in the same place you call Queue.
- Inspect any error returned before this one (br.err / the previous Exec return) — a server error earlier in the batch will make every subsequent read surface 'no more results in batch' as a secondary symptom.
- If you conditionally skip Queue calls, conditionally skip the matching reads in the same branch.
Example fix
// before
br := conn.SendBatch(ctx, batch)
for i := 0; i < 5; i++ { // hardcoded count drifted from batch.Len()
_, err := br.Exec()
if err != nil { return err } // hits "no more results in batch"
}
br.Close()
// after
br := conn.SendBatch(ctx, batch)
for i := 0; i < batch.Len(); i++ {
_, err := br.Exec()
if err != nil { return err }
}
return br.Close() Defensive patterns
Strategy: validation
Validate before calling
if batch.Len() == 0 { return nil }
readCount := 0
br := conn.SendBatch(ctx, batch)
for readCount < batch.Len() {
if _, err := br.Exec(); err != nil { _ = br.Close(); return err }
readCount++
}
return br.Close() Try / catch
br := conn.SendBatch(ctx, batch)
defer func() { _ = br.Close() }()
for i := 0; i < batch.Len(); i++ {
if _, err := br.Exec(); err != nil {
if err.Error() == "no more results in batch" {
// earlier query failed and aborted the batch; check prior error
}
return err
}
} Prevention
- Prefer QueuedQuery callbacks over manual Exec/Query loops so pgx tracks the read index.
- Always gate SendBatch on batch.Len()>0.
- Stop reading immediately when any result returns an error.
When it happens
Trigger: Calling Exec() on the BatchResults returned by Conn.SendBatch more times than Batch.Queue was called. Also reachable when an earlier query in the batch errors on the server: PostgreSQL aborts the implicit transaction and emits no result sets for the remaining queued queries, so a subsequent Exec() finds nothing.
Common situations: Mismatch between the number of Queue() calls and the number of Exec()/Query()/Close()-driven reads; a failed earlier statement that short-circuits the batch; upgrading a loop that previously added an extra read for a trailing row count.
Related errors
AI-assisted analysis of jackc/pgx@ec1a0befd2 (2026-08-04).
Data as JSON: /data/errors/ba3d8856d72b6230.json.
Report an issue: GitHub.