{"record":{"id":"fc392691f035e301","repo":"hasura/graphql-engine","slug":"database-locked","errorCode":null,"errorMessage":"database locked","messagePattern":"database locked","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/migrate/migrate.go","lineNumber":45,"sourceCode":"\t\"github.com/hasura/graphql-engine/cli/v2/util\"\n\tlog \"github.com/sirupsen/logrus\"\n\t\"golang.org/x/term\"\n)\n\n// DefaultPrefetchMigrations sets the number of migrations to pre-read\n// from the source. This is helpful if the source is remote, but has little\n// effect for a local source (i.e. file system).\n// Please note that this setting has a major impact on the memory usage,\n// since each pre-read migration is buffered in memory. See DefaultBufferSize.\nvar DefaultPrefetchMigrations = uint64(10)\n\n// DefaultLockTimeout sets the max time a database driver has to acquire a lock.\nvar DefaultLockTimeout = 15 * time.Second\n\nvar (\n\tErrNoChange         = errors.New(\"no change\")\n\tErrNilVersion       = errors.New(\"no migration\")\n\tErrLocked           = errors.New(\"database locked\")\n\tErrNoMigrationFiles = errors.New(\"no migration files found\")\n\tErrLockTimeout      = errors.New(\"timeout: can't acquire database lock\")\n\tErrApplied          = errors.New(\"Version already applied in database\")\n\tErrNotApplied       = errors.New(\"Migration not applied in database\")\n\tErrNoMigrationMode  = errors.New(\"Migration mode is disabled\")\n\tErrMigrationMode    = errors.New(\"Migration mode is enabled\")\n)\n\nconst (\n\tapplyingMigrationsMessage = \"Applying migrations\"\n)\n\nfunc newProgressBar(str string, w io.Writer, pbLogs bool) *pb.ProgressBar { //nolint:unparam\n\t// Default behaviour in non-interactive mode\n\tif !pbLogs && !term.IsTerminal(int(os.Stdout.Fd())) {\n\t\treturn nil\n\t}\n\t// bar template configuration","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/cli/migrate/migrate.go#L27-L63","documentation":"ErrLocked is returned by Lock() when the migration advisory/consul lock is already held by another client, meaning concurrent migration operations are not possible.","triggerScenarios":"Calling Lock() (or an operation that acquires it) while another CLI process or server instance holds the migration lock; some drivers return this immediately instead of waiting.","commonSituations":"Two `hasura migrate apply` runs racing, a hung previous process still holding the lock, CI jobs overlapping on the same server.","solutions":["Find and stop the other process holding the lock, or wait for it to finish","Set an appropriate lock timeout so Lock waits instead of failing fast","Serialize CI/CD migration steps with an external lock or --lock-timeout"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"func IsLocked(err error) bool { return errors.Is(err, migrate.ErrLocked) }","tryCatchPattern":"err := m.Lock()\nif err != nil {\n    if errors.Is(err, migrate.ErrLocked) { waitAndRetry() }\n    return err\n}","preventionTips":["Serialize migration runs in CI with an external lock","Set a lock timeout so waits happen server-side instead of failing fast"],"tags":["hasura","migration","locking","concurrency"],"backgroundTag":"database-lock-contention","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}