jackc/pgx · error
batch already closed
Error message
batch already closed
What it means
batchResults guard: Exec/Query was called on a batchResults that has already been closed — its results were consumed (or abandoned) and the underlying MultiResultReader is finished. The stale batchResults handle is the faulty input; each result can only be read once.
Solutions
- Read each batch result exactly once, before calling Close
- Use Exec/Query return values rather than re-querying the same batchResults
- Track the batch lifecycle explicitly when conditionally consuming results
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at batch.go:135 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of jackc/pgx@ec1a0befd2 (2026-08-04).
Data as JSON: /api/errors/97262baee492e6e0.
Report an issue: GitHub.
Appendix: source
Thrown at batch.go:135
type batchResults struct {
ctx context.Context
conn *Conn
mrr *pgconn.MultiResultReader
err error
b *Batch
qqIdx int
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
}View on GitHub (pinned to ec1a0befd2)