jackc/pgx · error

tx is closed

Error message

tx is closed

What it means

Sentinel ErrTxClosed: the operation (Begin, Commit, Rollback, etc.) was invoked on a transaction that is already closed — committed, rolled back, or otherwise finished. The stale Tx handle is the faulty input; detect it with errors.Is since it may be returned directly or wrapped.

Solutions

  1. Use the Tx only between Begin and Commit/Rollback; do not reuse it afterwards
  2. Track transaction state in calling code instead of retrying operations on a finished Tx
  3. Check with errors.Is(err, pgx.ErrTxClosed) where relevant
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at tx.go:85 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/d9e7f9b2d8583d1c. Report an issue: GitHub.

Appendix: source

Thrown at tx.go:85

	buf.WriteString("begin")

	if txOptions.IsoLevel != "" {
		buf.WriteString(" isolation level ")
		buf.WriteString(string(txOptions.IsoLevel))
	}
	if txOptions.AccessMode != "" {
		buf.WriteByte(' ')
		buf.WriteString(string(txOptions.AccessMode))
	}
	if txOptions.DeferrableMode != "" {
		buf.WriteByte(' ')
		buf.WriteString(string(txOptions.DeferrableMode))
	}

	return buf.String()
}

var ErrTxClosed = errors.New("tx is closed")

// ErrTxCommitRollback occurs when an error has occurred in a transaction and
// Commit() is called. PostgreSQL accepts COMMIT on aborted transactions, but
// it is treated as ROLLBACK.
var ErrTxCommitRollback = errors.New("commit unexpectedly resulted in rollback")

// Begin starts a transaction. Unlike [database/sql], the context only affects the begin command. i.e. there is no
// auto-rollback on context cancellation.
func (c *Conn) Begin(ctx context.Context) (Tx, error) {
	return c.BeginTx(ctx, TxOptions{})
}

// BeginTx starts a transaction with txOptions determining the transaction mode. Unlike [database/sql], the context only
// affects the begin command. i.e. there is no auto-rollback on context cancellation.
func (c *Conn) BeginTx(ctx context.Context, txOptions TxOptions) (Tx, error) {
	_, err := c.Exec(ctx, txOptions.beginSQL())
	if err != nil {
		// begin kills the connection upon receiving fatal, panic or non PGError errors,

View on GitHub (pinned to ec1a0befd2)