golang-migrate/migrate · error

max retries exceeded

Error message

max retries exceeded

What it means

The yugabytedb driver returns ErrMaxRetriesExceeded ('max retries exceeded') after its bounded retry loop (DefaultMaxRetries = 10) fails to perform a locked operation on the migrations/lock tables, typically while acquiring the advisory/table lock or ensuring the schema_migrations table exists. It signals repeated transient failures (connections, contention) rather than a single bad request.

Source

Thrown at database/yugabytedb/yugabytedb.go:33

	"github.com/golang-migrate/migrate/v4"
	"github.com/golang-migrate/migrate/v4/database"
	"github.com/jackc/pgconn"
	"github.com/jackc/pgerrcode"
	"github.com/lib/pq"
)

const (
	DefaultMaxRetryInterval    = time.Second * 15
	DefaultMaxRetryElapsedTime = time.Second * 30
	DefaultMaxRetries          = 10
	DefaultMigrationsTable     = "migrations"
	DefaultLockTable           = "migrations_locks"
)

var (
	ErrNilConfig          = errors.New("no config")
	ErrNoDatabaseName     = errors.New("no database name")
	ErrMaxRetriesExceeded = errors.New("max retries exceeded")
)

func init() {
	db := YugabyteDB{}
	database.Register("yugabyte", &db)
	database.Register("yugabytedb", &db)
	database.Register("ysql", &db)
}

type Config struct {
	MigrationsTable     string
	LockTable           string
	ForceLock           bool
	DatabaseName        string
	MaxRetryInterval    time.Duration
	MaxRetryElapsedTime time.Duration
	MaxRetries          int
}

View on GitHub (pinned to 01a9643f14)

Solutions

  1. Verify connectivity to the cluster and that the database/table exist and are reachable before retrying.
  2. Run migrations again once the cluster is healthy — the error is retryable by design.
  3. Check for concurrent migrate processes holding the lock table; ensure previous migrations released the lock.
  4. Increase retry headroom by tuning the driver's retry config if supported, or run migrations during a quieter window.
  5. Inspect YB server logs/tablet issues if the error recurs consistently.

Example fix

// before
if err := migrate.Up(); err != nil { log.Fatal(err) }
// after
if err := migrate.Up(); err != nil {
	if errors.Is(err, yugabytedb.ErrMaxRetriesExceeded) {
		log.Println("transient lock/table contention, retrying in 30s...")
		time.Sleep(30 * time.Second)
		return migrate.Up()
	}
	log.Fatal(err)
}
Defensive patterns

Strategy: retry

Validate before calling

// pre-check: database reachable and migrations table present
if _, err := conn.Query("SELECT 1"); err != nil {
	return fmt.Errorf("cluster unreachable before migrate: %w", err)
}

Try / catch

if err := migrate.Up(); err != nil {
	if errors.Is(err, yugabytedb.ErrMaxRetriesExceeded) {
		// wait and retry; check for stale locks in the lock table first
	}
}

Prevention

When it happens

Trigger: Retries exhausted while ensuring the migrations table or acquiring the lock table row in YugabyteDB; unstable connectivity to the cluster; very slow DDL/lock operations in YB so every attempt times out.

Common situations: Migrating against a YugabyteDB cluster under load or during tablet rebalancing; network flakiness between CI and the cluster; long-running migrations holding the lock table; running many concurrent migrate processes.

Related errors


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