golang-migrate/migrate · warning
conn: %v, db: %v
Error message
conn: %v, db: %v
What it means
Close shuts down both the underlying sql.DB and the dedicated migration connection and, if either Close fails, returns a combined 'conn: %v, db: %v' message showing both error values. One of the two may print as <nil>; the format always includes both so you can tell which half failed. It is raised in Mysql.Close (database/mysql/mysql.go:280).
Source
Thrown at database/mysql/mysql.go:280
NoLock: noLock,
StatementTimeout: time.Duration(statementTimeout) * time.Millisecond,
})
if err != nil {
return nil, err
}
return mx, nil
}
func (m *Mysql) Close() error {
connErr := m.conn.Close()
var dbErr error
if m.db != nil {
dbErr = m.db.Close()
}
if connErr != nil || dbErr != nil {
return fmt.Errorf("conn: %v, db: %v", connErr, dbErr)
}
return nil
}
func (m *Mysql) Lock() error {
return database.CasRestoreOnErr(&m.isLocked, false, true, database.ErrLocked, func() error {
if m.config.NoLock {
return nil
}
aid, err := database.GenerateAdvisoryLockId(
fmt.Sprintf("%s:%s", m.config.DatabaseName, m.config.MigrationsTable))
if err != nil {
return err
}
query := "SELECT GET_LOCK(?, 10)"
var success bool
if err := m.conn.QueryRowContext(context.Background(), query, aid).Scan(&success); err != nil {View on GitHub (pinned to 01a9643f14)
Solutions
- Inspect the non-<nil> part of the message to see whether the dedicated conn or the pool failed
- Ensure you are not closing the *sql.DB yourself before handing it to the driver / calling driver Close
- Check server logs and network health; usually the underlying connection was already dead and cleanup can be treated as best-effort
- Call Close only once per driver instance
Example fix
// before
db.Close()
drv, _ := mysql.WithInstance(db, cfg)
...
drv.Close() // double-close -> "conn: ..., db: sql: database is closed"
// after
drv, _ := mysql.WithInstance(db, cfg)
...
if err := drv.Close(); err != nil {
log.Printf("migration close (best-effort): %v", err)
}
db.Close() Defensive patterns
Strategy: try-catch
Try / catch
if err := drv.Close(); err != nil {
// message is "conn: %v, db: %v" — parse which side failed
log.Printf("migrate close (best-effort, check conn vs db part): %v", err)
}
// ensure single ownership: only the driver closes resources it opened;
// you close the *sql.DB yourself after driver Close Prevention
- Call Close exactly once per driver instance
- Never close the *sql.DB yourself before calling driver Close if the driver owns it
- Treat Close errors during shutdown as best-effort and log rather than fail the process
- Monitor server-side connection kills/timeouts that leave connections half-dead
When it happens
Trigger: Calling Close (directly or via defer) after Open/WithInstance succeeded, when the dedicated conn.Close() or db.Close() returns an error, e.g. the connection is already broken or the pool was already closed elsewhere.
Common situations: Network drop between migration end and Close; closing the *sql.DB yourself before calling driver Close (double-close); timeouts during shutdown of an app holding an idle-pooled connection that has been killed server-side.
Related errors
AI-assisted analysis of golang-migrate/migrate@01a9643f14 (2026-09-02).
Data as JSON: /api/errors/de275495c9dbd815.
Report an issue: GitHub.