Flowable/Activiti Won't Start: liquibase - Waiting for changelog lock....

If you use the Flowable/Activiti process engine and the application hangs on startup with the error 'liquibase - Waiting for changelog lock....', it means a database table's lock was never released. This isn't a database-level table lock, so you won't find any locked table by querying — it's a logical lock.

If you use the Flowable/Activiti process engine and the application hangs on startup with the error liquibase - Waiting for changelog lock...., it means a database table’s lock was never released. This isn’t a database-level table lock, so you won’t find any locked table by querying — it’s a logical lock.

The fix

Check these database tables. The queries below matter only for the locked field — a value of 1 means the lock is still held:

SELECT locked FROM act_app_databasechangeloglock;
SELECT locked FROM act_cmmn_databasechangeloglock;
SELECT locked FROM act_co_databasechangeloglock;
SELECT locked FROM act_dmn_databasechangeloglock;
SELECT locked FROM act_fo_databasechangeloglock;
SELECT locked FROM flw_ev_databasechangeloglock;

Unlocking is just a matter of resetting that value. Here’s an unlock template — adapt it to your situation:

UPDATE act_app_databasechangeloglock SET locked = 0, lockgranted = NULL, lockedby = NULL WHERE id = 1;
UPDATE act_cmmn_databasechangeloglock SET locked = 0, lockgranted = NULL, lockedby = NULL WHERE id = 1;
UPDATE act_co_databasechangeloglock SET locked = 0, lockgranted = NULL, lockedby = NULL WHERE id = 1;
UPDATE act_dmn_databasechangeloglock SET locked = 0, lockgranted = NULL, lockedby = NULL WHERE id = 1;
UPDATE act_fo_databasechangeloglock SET locked = 0, lockgranted = NULL, lockedby = NULL WHERE id = 1;
UPDATE flw_ev_databasechangeloglock SET locked = 0, lockgranted = NULL, lockedby = NULL WHERE id = 1;

If you just want the problem solved, you can stop here. Below, let’s dig into why this happens.

Why it happens

What you’ve triggered is Liquibase’s database version control. LiquiBase is an open source tool for database refactoring and migration: it records database changes in a changelog file, then applies the changes in that file to update or roll the database back to a consistent state.

The DATABASECHANGELOGLOCK table controls concurrent access while Liquibase applies changes. When Liquibase applies changes, it automatically creates a row in this table to prevent other processes or threads from modifying the database at the same time. Once the changes are applied, Liquibase deletes that row to let other processes proceed.

If we force the application to exit — a kill -9, or hitting the stop button twice while debugging locally — the process dies hard. If a database change was executing at that exact moment, the lock never gets deleted, and it stays there forever. The next startup then waits endlessly for a lock release that will never come.

All of which is to say: when shutting down your application, it’s best to let it finish cleanly. Force-killing processes can lead to some very strange problems.