kestra-io/kestra · error · MigrationLockedException
Migration lock is held by another process. Another instance
Error message
Migration lock is held by another process. Another instance may be running migrations. If this is unexpected, use 'kestra migrate unlock' to force-release the lock.
What it means
Thrown by MigrationRunner.runOrFailIfLocked() when a distributed migration lock (MigrationLock) cannot be acquired via tryAcquire(), meaning another process or Kestra node currently holds it. This is the guard that prevents two nodes from running schema migrations concurrently and corrupting the migration history table. The thrown MigrationLockedException carries no extra payload; the actionable guidance lives in the message: run 'kestra migrate unlock' to force-release a stale lock.
Source
Thrown at core/src/main/java/io/kestra/core/migration/MigrationRunner.java:115
try {
executeMigrations();
} finally {
lock.release();
}
hasRun = true;
}
@Override
public void runOrFailIfLocked() throws MigrationLockedException, Exception {
if (hasRun) {
return;
}
if (scripts.isEmpty()) {
log.debug("No migration scripts found, skipping migration.");
return;
}
if (!lock.tryAcquire()) {
throw new MigrationLockedException();
}
try {
executeMigrations();
} finally {
lock.release();
}
hasRun = true;
}
/**
* Re-synchronizes the stored checksum for a migration script that has already been applied.
*
* @param scriptId the migration script ID to repair
* @throws MigrationLockedException if another process holds the migration lock
* @throws Exception if the script is unknown, unapplied, unsupported, or the backend update fails
*/
@Override
public void repairChecksum(final String scriptId) throws MigrationLockedException, Exception {View on GitHub (pinned to 823fada927)
Solutions
- Confirm no other node/process is genuinely running migrations (check process list / other node logs).
- Run 'kestra migrate unlock' to force-release the stale lock.
- If using a JDBC backend, verify the lock row in the migration lock table is cleared after unlock.
- Add a startup ordering strategy so only one node runs migrations on cluster bootstrap.
Example fix
// before: two nodes race the lock nodeA: runOrFailIfLocked() // throws MigrationLockedException // after: release stale lock, then retry // $ kestra migrate unlock // then restart the node, or call autoRun() again
Defensive patterns
Strategy: retry
Validate before calling
// Before startup, check if the lock is already held
boolean canProceed = migrationLock.tryAcquire();
if (!canProceed) {
log.warn("Migration lock held; deferring startup or run 'kestra migrate unlock'.");
}
// Release immediately if you only probed
if (canProceed) migrationLock.release(); Try / catch
try {
migrationRunner.runOrFailIfLocked();
} catch (MigrationLockedException e) {
log.error("Could not acquire migration lock. Another node may be migrating. "
+ "If stale, run: kestra migrate unlock", e);
// Optionally: surface to operator / retry with backoff / fail startup
throw e;
} Prevention
- Ensure only one node performs startup migrations in a multi-node cluster.
- Always use 'kestra migrate unlock' after an unclean shutdown before restarting.
- Monitor the migration lock table/entry for stale owners.
When it happens
Trigger: Calling runOrFailIfLocked() while a second Kestra instance is already mid-migration; a previous migration crashed or was killed leaving the lock unreleased; the kestra migrate CLI was interrupted; on startup of a multi-node cluster where another node won the lock race.
Common situations: Multi-node deployments where nodes boot near-simultaneously; a migration that errored and the JVM was force-killed before the finally{lock.release()} ran; manual CLI migration runs left orphaned locks after a timeout; restart loops during a bad deploy.
Related errors
- {} not started in time
- No log store configured through the application property '%s
- Unable to hold the lock inside the configured timeout of {}
- Migration script [<scriptId>] has no checksum to repair.
- Cannot repair migration script [<scriptId>] because it has n
AI-assisted analysis of kestra-io/kestra@823fada927 (2026-08-14).
Data as JSON: /api/errors/d8a78cc734e3da6c.
Report an issue: GitHub.