golang-migrate/migrate · warning
conn: %v, db: %v
Error message
conn: %v, db: %v
What it means
Close() on the Postgres driver closes both the underlying *sql.DB and the raw *sql.Conn. If either close returns an error, both errors are combined into one 'conn: %v, db: %v' message, including nil for whichever side succeeded. It indicates the database resources may not have been released cleanly.
Source
Thrown at database/postgres/postgres.go:227
MultiStatementMaxSize: multiStatementMaxSize,
})
if err != nil {
return nil, err
}
return px, nil
}
func (p *Postgres) Close() error {
connErr := p.conn.Close()
var dbErr error
if p.db != nil {
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)}
}
View on GitHub (pinned to 01a9643f14)
Solutions
- Inspect both sub-errors in the message; if one says 'sql: database is closed' the caller closed the handle first — only close one side
- Check network/firewall stability to the PostgreSQL server and retry Close once after reconnect logic if needed
- Treat non-nil conn/db errors during process shutdown as warnings if the process is exiting and the OS will reclaim sockets
Example fix
// before
if err := driver.Close(); err != nil { panic(err) }
// after
if err := driver.Close(); err != nil {
if !errors.Is(err, sql.ErrConnDone) && !strings.Contains(err.Error(), "database is closed") {
log.Printf("migrator close failed: %v", err)
}
} Defensive patterns
Strategy: try-catch
Validate before calling
if instance.db == nil || instance.conn == nil {
return errors.New("migrator already closed or not fully initialized")
} Try / catch
err := driver.Close()
if err != nil {
var parts struct{ conn, db error }
if _, scanErr := fmt.Sscanf(err.Error(), "conn: %v, db: %v", &parts.conn, &parts.db); scanErr == nil {
if !strings.Contains(err.Error(), "sql: database is closed") {
log.Printf("unexpected close error: %v", err)
}
}
} Prevention
- Give the migrator sole ownership of the *sql.DB you pass to WithInstance
- Avoid calling Close twice; track closed state with sync.Once
- Treat close errors during process exit as best-effort warnings
When it happens
Trigger: Calling Driver.Close() when the TCP connection to PostgreSQL was already broken, the server rejected the termination, or the database handle was already closed.
Common situations: Closing the migrator after a network drop or server restart; double-closing resources because the caller also closed the *sql.DB it passed to WithInstance; timeouts during shutdown.
Related errors
AI-assisted analysis of golang-migrate/migrate@01a9643f14 (2026-09-02).
Data as JSON: /api/errors/5598c16d271457dc.
Report an issue: GitHub.