Liquibase "liquibase.exception.LockException" in CI
By Daniel Zoghalchali·Latchkey
Liquibase threw a LockException after failing to acquire the changelog lock. The lock is held - typically because a previous run died without releasing it, or another migration is running concurrently.
What this error means
The Liquibase step fails with LockException: Could not acquire change log lock, naming who holds it and since when. Unlike the silent "waiting" hang, this raises and fails the job.
liquibase output
Unexpected error running Liquibase: Could not acquire change log lock.
Currently locked by runner-3 (10.0.0.3) since 2026-06-25 08:02
liquibase.exception.LockException: Could not acquire change log lock.
Common causes
Stale lock left by a killed run
A cancelled, timed-out, or OOM-killed run never released the lock, so the DATABASECHANGELOGLOCK row stays locked and the next attempt cannot acquire it.
Concurrent migration in progress
Another pipeline or process is actively migrating the same database and currently holds the lock.
How to fix it
Confirm no live run, then release the lock
Check the lock owner/timestamp; if it is from a dead job, release it and retry.
Terminal
liquibase releaseLocks # only when no other migration is active
liquibase update
Prevent overlap with concurrency control
Put migration jobs in a CI concurrency group so only one runs at a time.
Make jobs idempotent and able to release the lock on cancellation.
Set a step timeout so a stuck acquisition fails fast and surfaces the stale lock.
How to prevent it
Serialize migrations with a concurrency group.
Clear stale locks at the start of a controlled, single-writer deploy.
Use timeouts so a stuck lock acquisition fails fast instead of hanging.
Frequently asked questions
What causes ""liquibase.exception.LockException""?
A cancelled, timed-out, or OOM-killed run never released the lock, so the DATABASECHANGELOGLOCK row stays locked and the next attempt cannot acquire it.
How do I fix "liquibase.exception.LockException"?
Check the lock owner/timestamp; if it is from a dead job, release it and retry.
Can Latchkey fix this automatically?
Yes. Latchkey runs your GitHub Actions on managed runners that detect this failure, apply the fix, and retry the job automatically - self-healing is on by default.