“Past-due actions found; something may be wrong” — the fix
The notice means jobs WooCommerce queued more than a day ago still have not run. Nothing is corrupt; the runner that should process them is not being started. Here is what the queue is, why it stalls, how to drain it, and how to keep it from filling again.
What exactly triggers the notice
Action Scheduler: 1,240 past-due actions found; something may be wrong. Read documentation »
Action Scheduler is the job queue inside WooCommerce. Anything that should happen later or in the background — sending a webhook, running an import in chunks, generating analytics, a subscription renewal, an abandoned-cart email from a plugin — is written to the queue as a “pending action” with a date. A runner is supposed to pick up due actions every minute and execute them.
The notice is printed on admin pages when at least one pending action has a scheduled date more than 24 hours in the past. That is the whole test: pending, and overdue by a day. It is checked at most every six hours, which is why the number on the notice can lag behind what the list shows. It says nothing about why the actions did not run; it only says the runner has not got to them.
Click the past-due actions link, or go to WooCommerce → Status → Scheduled Actions and filter by Past-due. The hook names in that list tell you what is not happening on your shop right now: woocommerce_deliver_webhook_async means webhooks are not being delivered, wc-admin_import_orders means Analytics is behind, and so on.
How the runner is supposed to start
Action Scheduler registers a WP-Cron event, action_scheduler_run_queue, on a one-minute schedule. Each run processes a batch of up to 25 actions and stops after 30 seconds, then the next minute’s run continues. Only one batch runs at a time. There is also an async runner that fires a background request when an admin page loads and actions are due, which is why a stalled queue often lurches forward when somebody logs in.
WP-Cron is not a real cron. It runs when a visitor requests a page, on that visitor’s request. So the entire mechanism depends on two things: page views arriving regularly, and each of those page views being allowed to spawn the cron request. When either fails, the queue fills and the notice appears.
The five causes, in the order to check them
- DISABLE_WP_CRON is set in wp-config.php and no system cron replaced it. Hosts and caching guides recommend the constant; the second half of the advice — “and add a real cron job” — is the part that gets skipped. Nothing runs, ever, except on admin page loads.
- The site gets too little traffic to trigger WP-Cron. A B2B shop with thirty visits a day gets thirty chances a day to run a 30-second batch. A queue that receives more work than that never catches up.
- Page cache serves every page without touching PHP. If Cloudflare, Varnish or a full-page cache plugin serves the whole site from cache, WP-Cron is never spawned by visitors at all; only uncached requests count.
- The cron request cannot reach the site. WP-Cron works by the site making an HTTP request to its own wp-cron.php. A firewall, HTTP authentication on a staging site, a wrong siteurl, or DNS that resolves the domain to a different server from inside the box all break that loopback silently. WooCommerce → Status → System status → WordPress environment shows a WP-Cron loopback test.
- The batch keeps dying before it finishes. A single action that fatals or hits the PHP memory limit takes the batch down with it, and the same action is picked up first next time. The queue looks stalled while the runner is actually being killed once a minute. Check the error log, and look for one hook name at the top of the past-due list with a very old date.
Draining the queue
Once the cause is fixed, the backlog still has to run. A few hundred actions will clear on their own within an hour of a working cron. Thousands will not, because the runner only gets 30 seconds a minute and other work keeps arriving. Run it from the command line, where it has no time limit:
# Process everything that is due, 100 at a time, until the queue is empty
wp action-scheduler run --batch-size=100
# Only the webhook deliveries, if that is what matters right now
wp action-scheduler run --hooks=woocommerce_deliver_webhook_asyncBefore you drain, decide whether every one of those actions should still happen. Twelve hundred abandoned-cart emails from three weeks ago should not go out today. Filter the list by hook, and for hooks whose actions are no longer wanted, use Cancel on the list rather than running them. Cancelled actions are kept for the log and then removed by the cleaner.
If the actionscheduler_actions table itself has grown to millions of rows, the queue will be slow even when it works, because every query scans it. The cleaner removes completed actions after a month and failed ones after three; a long-broken queue can accumulate far more than that. wp action-scheduler clean, or deleting rows with status complete or canceled older than a month, brings it back.
Keeping it from filling again: a real cron
The durable fix is the same for every cause: stop depending on visitors. Disable WP-Cron’s visitor trigger and have the server call it on a schedule instead.
# wp-config.php
define( 'DISABLE_WP_CRON', true );
# crontab -e (every minute, as the web user)
* * * * * cd /var/www/shop && wp cron event run --due-now >/dev/null 2>&1
# Or, without WP-CLI, request the file directly
* * * * * curl -s https://shop.example/wp-cron.php?doing_wp_cron >/dev/null 2>&1Every minute is right, not every five: Action Scheduler’s own schedule is per minute, and a runner that is called less often simply does less work. On a busy shop, add a second entry that runs wp action-scheduler run directly so the queue is not limited to WP-Cron’s 30-second batches at all.
The checklist
- WooCommerce → Status → Scheduled Actions → Past-due: which hooks, how old is the oldest.
- wp-config.php: is DISABLE_WP_CRON set, and if so, is there a system cron actually calling the site?
- WooCommerce → Status: does the WP-Cron loopback check pass?
- Full-page cache: does any request reach PHP without a logged-in user?
- Error log: is one action fataling and killing every batch?
- Drain with wp action-scheduler run; cancel what should no longer happen.
- Install a per-minute system cron so it does not happen again.
What was lost while the queue was stalled
Most queued work is harmless to run late. Webhooks are the exception. An order.created webhook delivered three days after the order is a fulfilment system, a CRM or an accounting tool that learned about the sale three days late — or, if the webhook had already been switched off after failed deliveries, never learned at all. Action Scheduler keeps one line per attempt; it does not tell you which orders the other system is still missing.
