gofr-dev/gofr · error
failed to release migration lock
Error message
failed to release migration lock
What it means
errLockReleaseFailed is returned by unlock() when the migration lock cannot be released after migrations finish. Subsequent migrations will be blocked until the lock expires or is removed manually. It signals a Redis error or that the lock was already lost to another holder.
Source
Thrown at pkg/gofr/migration/migration.go:20
import (
"context"
"errors"
"reflect"
"sort"
"time"
"github.com/gogo/protobuf/sortkeys"
"github.com/google/uuid"
goRedis "github.com/redis/go-redis/v9"
"gofr.dev/pkg/gofr/container"
gofrSql "gofr.dev/pkg/gofr/datasource/sql"
)
var (
errLockAcquisitionFailed = errors.New("failed to acquire migration lock")
errLockReleaseFailed = errors.New("failed to release migration lock")
)
const (
// lockKey is the key used for distributed locking.
lockKey = "gofr_migrations_lock"
// Default values for configuration.
defaultRetry = 500 * time.Millisecond
// defaultLockTTL is the duration for which the migration lock is valid.
// It is kept at 15 seconds to provide a safety margin for network jitters or transient failures.
defaultLockTTL = 15 * time.Second
// defaultRefresh is the interval at which the migration lock is renewed.
// A 5-second interval allows for up to 2 failed refresh attempts before the 15-second TTL expires,
// ensuring the lock stays robust while still allowing fairly quick recovery if a process crashes.
defaultRefresh = 5 * time.Second
)
type MigrateFunc func(d Datasource) errorView on GitHub (pinned to 187eb24962)
Solutions
- Manually delete gofr_migrations_lock if no migration is running
- Verify Redis connectivity/auth and inspect client errors in logs
- Increase lock TTL or use heartbeat renewal for long migrations
- Re-run the next migration attempt once the lock is free
Example fix
// before // after failed unlock: redis-cli EXISTS gofr_migrations_lock shows stale key // after redis-cli DEL gofr_migrations_lock
Defensive patterns
Strategy: try-catch
Validate before calling
if redisClient == nil || redisClient.Ping(ctx).Err() != nil {
return errors.New("redis unavailable; lock release would fail")
} Try / catch
err := migrator.Run(c)
if errors.Is(err, migration.ErrLockReleaseFailed) {
log.Warn("migration succeeded but lock release failed; clear gofr_migrations_lock manually")
// cleanup: redis-cli DEL gofr_migrations_lock
} Prevention
- Delete the lock key manually if migrations completed but unlock failed
- Use heartbeat renewal / adequate TTL for long migrations
- Alert on unlock failures so stale locks are caught early
- Keep the Redis session alive across the whole migration (no idle timeouts)
When it happens
Trigger: Redis DEL (or compare-and-delete) on gofr_migrations_lock fails: client error, connection loss, or the key no longer matches this holder.
Common situations: Redis restarted mid-migration losing key semantics; network partition at unlock time; lock TTL expired during a very long migration and another node took it.
Related errors
- failed to acquire migration lock
- failed to refresh MongoDB lock: lock lost or stolen
- elasticsearch: %w
- failed to record migration: %w
- mongo: %w
AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01).
Data as JSON: /api/errors/f146782de7509895.
Report an issue: GitHub.