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 nilView on GitHub (pinned to 01a9643f14)
Solutions
- Read both parts of the message: the side showing <nil> succeeded, the other side names the real failure
- Do not close the *sql.DB yourself if you handed it to WithConnection — let the driver own it
- Call Close exactly once per driver instance; ignore/handle 'already closed' errors defensively
- 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
- Call Close exactly once per driver instance
- Do not close the *sql.DB yourself after passing it to WithConnection
- Defer Close and log (not fatal) — close errors after a successful run are usually benign
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.