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

  1. Inspect both sub-errors in the message; if one says 'sql: database is closed' the caller closed the handle first — only close one side
  2. Check network/firewall stability to the PostgreSQL server and retry Close once after reconnect logic if needed
  3. 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

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.