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
- Verify connectivity to the cluster and that the database/table exist and are reachable before retrying.
- Run migrations again once the cluster is healthy — the error is retryable by design.
- Check for concurrent migrate processes holding the lock table; ensure previous migrations released the lock.
- Increase retry headroom by tuning the driver's retry config if supported, or run migrations during a quieter window.
- 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
- Run migrations serially — avoid concurrent migrate processes contending for the lock table
- Ensure network stability between CI and the YugabyteDB cluster
- Check for stale lock rows in the lock table after crashed runs
- Schedule migrations outside peak load / tablet rebalancing windows
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
- no config
- no database name
- digits must be positive
- the seq and format options are mutually exclusive
- time format may not be empty
AI-assisted analysis of golang-migrate/migrate@01a9643f14 (2026-09-02).
Data as JSON: /api/errors/e72ebd3dcd5baec9.
Report an issue: GitHub.