golang-migrate/migrate · warning

conn: %v, db: %v

Error message

conn: %v, db: %v

What it means

Close() on the Redshift driver closes both the raw connection and the database handle, then reports any failures combined as 'conn: %v, db: %v' (with <nil> for the healthy side). Redshift does not support postgres advisory locks, so cleanup correctness relies solely on these close calls; a failure here means connections may linger until the server times them out.

Source

Thrown at database/redshift/redshift.go:122

	migrationsTable := purl.Query().Get("x-migrations-table")

	px, err := WithInstance(db, &Config{
		DatabaseName:    purl.Path,
		MigrationsTable: migrationsTable,
	})
	if err != nil {
		return nil, err
	}

	return px, nil
}

func (p *Redshift) 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
}

// Redshift does not support advisory lock functions: https://docs.aws.amazon.com/redshift/latest/dg/c_unsupported-postgresql-functions.html
func (p *Redshift) Lock() error {
	if !p.isLocked.CompareAndSwap(false, true) {
		return database.ErrLocked
	}
	return nil
}

func (p *Redshift) Unlock() error {
	if !p.isLocked.CompareAndSwap(true, false) {
		return database.ErrNotLocked
	}
	return nil
}

View on GitHub (pinned to 01a9643f14)

Solutions

  1. Read both parts of the combined message; ignore 'sql: database is closed' if your code intentionally closed the *sql.DB first
  2. Ensure only one owner closes shared handles (pass the *sql.DB to the driver and let Close() own cleanup)
  3. Check VPC/security-group connectivity and Redshift's connection limits if closes consistently fail

Example fix

// before
db, _ := sql.Open(...)
d, _ := redshift.WithInstance(instance, cfg)
defer db.Close() // closes handle the driver also closes
// after
d, _ := redshift.WithInstance(instance, cfg)
defer func() {
    if err := d.Close(); err != nil {
        log.Printf("migrator close: %v", err)
    }
}() // single close owner
Defensive patterns

Strategy: try-catch

Validate before calling

if redshiftConn.conn == nil || redshiftConn.db == nil {
    return errors.New("redshift migrator already closed or not initialized")
}

Try / catch

if err := driver.Close(); err != nil {
    if strings.Contains(err.Error(), "sql: database is closed") {
        return nil // intentional prior close
    }
    return fmt.Errorf("closing redshift migrator: %w", err)
}

Prevention

When it happens

Trigger: Calling Driver.Close() after the Redshift connection was already terminated (idle timeout, admin intervention, network interruption) or when the *sql.DB was closed by the caller first.

Common situations: Long-running migration workers whose connections are reaped by Redshift's connection limits; Lambda/short-lived jobs where the caller's deferred close of *sql.DB runs before the driver's Close; network blips to the Redshift endpoint.

Related errors


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