golang-migrate/migrate · warning

conn: %v, db: %v

Error message

conn: %v, db: %v

What it means

Postgres.Close in the pgx/v5 driver closes both the pgx connection and the underlying database/sql pool. If either returns an error, it reports both values together as 'conn: %v, db: %v'. Even a nil error prints as 'conn: <nil>' because the message always includes both sides.

Source

Thrown at database/pgx/v5/pgx.go:215

		MigrationsTable:       migrationsTable,
		MigrationsTableQuoted: migrationsTableQuoted,
		StatementTimeout:      time.Duration(statementTimeout) * time.Millisecond,
		MultiStatementEnabled: multiStatementEnabled,
		MultiStatementMaxSize: multiStatementMaxSize,
	})

	if err != nil {
		return nil, err
	}

	return px, nil
}

func (p *Postgres) Close() error {
	connErr := p.conn.Close()
	dbErr := p.db.Close()
	if connErr != nil || dbErr != nil {
		return fmt.Errorf("conn: %v, db: %v", connErr, dbErr)
	}
	return nil
}

// https://www.postgresql.org/docs/9.6/static/explicit-locking.html#ADVISORY-LOCKS
func (p *Postgres) Lock() error {
	return database.CasRestoreOnErr(&p.isLocked, false, true, database.ErrLocked, func() error {
		aid, err := database.GenerateAdvisoryLockId(p.config.DatabaseName, p.config.migrationsSchemaName, p.config.migrationsTableName)
		if err != nil {
			return err
		}

		// This will wait indefinitely until the lock can be acquired.
		query := `SELECT pg_advisory_lock($1)`
		if _, err := p.conn.ExecContext(context.Background(), query, aid); err != nil {
			return &database.Error{OrigErr: err, Err: "try lock failed", Query: []byte(query)}
		}
		return nil

View on GitHub (pinned to 01a9643f14)

Solutions

  1. Read both parts of the message: the side showing <nil> succeeded, the other side names the real failure
  2. Do not close the *sql.DB yourself if you handed it to WithConnection — let the driver own it
  3. Call Close exactly once per driver instance; ignore/handle 'already closed' errors defensively
  4. If the error indicates a network problem, retry is usually pointless — the migration run is over; just log it

Example fix

// before
db.Close()
_ = d.Close() // may fail: conn already closed
// after
if err := d.Close(); err != nil {
    log.Printf("driver close: %v", err)
} // driver owns db; do not close db separately
Defensive patterns

Strategy: try-catch

Try / catch

if err := d.Close(); err != nil {
    // message is 'conn: <x>, db: <y>'; nil side prints <nil>
    if !strings.Contains(err.Error(), "already closed") {
        log.Printf("driver close error: %v", err)
    }
}

Prevention

When it happens

Trigger: Calling Close() on a driver obtained from WithInstance/WithConnection/Open when the pgx conn.Close() or db (sql.DB) Close() fails — e.g. the connection is already closed, the network dropped, or the pool was closed by the caller previously.

Common situations: Double-closing: application closes the sql.DB it passed to WithConnection and then calls driver.Close(); abrupt network disconnects during shutdown; leaking the driver without ever calling Close (opposite problem) so resources exhaust.

Related errors


AI-assisted analysis of golang-migrate/migrate@01a9643f14 (2026-09-02). Data as JSON: /api/errors/838fdebc7165f4a6. Report an issue: GitHub.