WordPress shows "Another update is currently in progress" when a row called core_updater.lock exists in wp_options and its timestamp is less than 15 minutes old. Wait 15 minutes, or delete it with wp option delete core_updater.lock, and the update screen works again.
Every guide on the first page stops there. This one reads the lock first, because the number inside it tells you whether deleting it is safe, and why it came back if it does.
Where the message comes from
The message is the locked string in Core_Upgrader, the class that updates WordPress core. Before it touches a file, it asks for a lock:
$lock = WP_Upgrader::create_lock( 'core_updater', 15 * MINUTE_IN_SECONDS );
create_lock() in class-wp-upgrader.php runs an INSERT IGNORE into wp_options with the option name core_updater.lock and the value time(), a plain Unix timestamp, stored with autoload off.
If the insert fails because the row already exists, core reads the timestamp. If it is younger than 15 minutes, you get the message.
If it is older, core deletes it and takes a fresh lock.
When the update finishes, release_lock() deletes the row. When the PHP process dies mid-update (a timeout, a memory limit, a closed tab on a slow host, a server restart), nothing deletes it.
The lock cannot outlive 15 minutes on its own
This is the part the ranking guides skip. Kinsta and WPBeginner both mention the 15-minute window, then move straight to phpMyAdmin.
But read the code again: an expired lock is cleared automatically the next time anyone starts a core update. A dead update leaves a stale row, and that row stops mattering after 15 minutes.
So if you still see the message more than 15 minutes after the last attempt, one of three things is true:
- An update really is running. A second admin, a management dashboard, or a background auto-update started it.
- Something keeps starting new updates, and each one dies and leaves a fresh lock.
- The timestamp is in the future, so it never looks old. This happens when the lock was written by a server whose clock was wrong, or when a database was copied from one.
Deleting the row fixes the third case. It hides the first two, and in the first one it lets two updates write over the same core files at once, which is exactly what the lock exists to stop.
Read the lock before you delete it
With WP-CLI, one line tells you how old the lock is, in seconds:
wp eval 'echo time() - (int) get_option( "core_updater.lock" ), PHP_EOL;'
How to read the result:
- Under 900: the lock is inside its 15-minute window. Something took it recently. Find out what before you remove it.
- Over 900: it is stale and core will clear it by itself on the next attempt. Just retry the update.
- Negative: the timestamp is in the future. Check the server clock with
date -u, fix it if it is wrong, then delete the row.
Without WP-CLI, run the same check in SQL and compare it with the current time:
SELECT option_value, UNIX_TIMESTAMP() FROM wp_options WHERE option_name = 'core_updater.lock';
Change wp_ if your site uses a different table prefix.
If it keeps coming back
A lock that reappears after you delete it means an update keeps starting. The usual source is the automatic updater, which runs from WP-Cron.
It has a lock of its own, auto_updater.lock. It is created without a timeout argument, so it uses the default in create_lock(): one hour.
That lock never shows a message: while it exists, background updates are simply skipped.
Check for both, and for the cron events that start them:
wp option get auto_updater.lock
wp cron event list --fields=hook,next_run_relative | grep -E 'version_check|update'
If an auto-update is dying every time it runs, the fix is whatever kills it: usually the PHP time limit or memory limit. Debug mode or WP_DEBUG_LOG will show the fatal, and deleting the lock only clears the evidence.
How to delete it safely
Once you know nothing is running, any of these works.
WP-CLI:
wp option delete core_updater.lock
SQL:
DELETE FROM wp_options WHERE option_name = 'core_updater.lock';
The "Fix Another Update In Progress" plugin does the same delete from a button. On a site with no shell and no database access, it is a reasonable option, and you can remove it afterwards.
Check what the dead update left behind
The lock is the harmless part. The update that died while holding it may have stopped halfway through copying core files.
Before you retry, check the files against the official checksums:
wp core version
wp core verify-checksums
If files are missing or modified, reinstall the same version over the top. This replaces core files and does not touch wp-content:
wp core download --version=$(wp core version) --force --skip-content
Then retry the update, and finish with wp core update-db in case the database upgrade never ran.
If the site is also showing "Briefly unavailable for scheduled maintenance", that is a separate file, not this lock. Our post on WordPress stuck in maintenance mode covers it.
How to stop it happening
Core updates die when the request that runs them is cut off. The command line is not subject to the web server's request timeout, so wp core update is the more reliable way to run a large update.
The better habit is to run the update on a copy first. Clone the live site into a disposable sandbox with the Cloner plugin, update it there, and see whether it finishes.
Every SandyWP sandbox has SSH and SFTP access, so every command in this post runs as written. If the update breaks the copy, you delete it and nothing live was touched.
A sandbox is not the fix for a live site that is locked right now. Use the steps above for that.
If you want tested changes pushed back to production, that is a staging job; the upgrade testing page explains where the line falls.
Quick reference
- Seen within 15 minutes of an update: wait. Something may still be running.
- Lock older than 900 seconds: retry the update. Core clears it by itself.
- Negative age: the server clock is wrong. Fix it, then delete the row.
- Comes back after deleting: find what keeps starting updates, starting with WP-Cron and
auto_updater.lock. - After any dead update: run
wp core verify-checksumsbefore you call it fixed.
