Troubleshooting
Tasks Not Executing
WP-Cron Is Not Running
Symptom: Tasks are registered but never execute.
Cause: WordPress's default cron relies on site visits. No traffic = no cron.
Solution: Set up a system-level cron job:
// wp-config.php
define('DISABLE_WP_CRON', true);
# System cron (every minute)
* * * * * curl -s https://yoursite.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1
ServiceProvider Not Registered
Symptom: $app->make(Schedule::class) throws an error.
Solution: Ensure the SchedulerServiceProvider is registered:
$app->register(new SchedulerServiceProvider());
Tasks Registered After Cron Hook
Symptom: Tasks are defined but getDueTasks() returns empty.
Solution: Register tasks before the cron hook fires. Use the ServiceProvider's boot() method or hook into an early action like plugins_loaded.
Task Executing Too Often or Not Often Enough
Minute-Level Precision
Symptom: Tasks execute approximately but not exactly at the specified time.
Cause: WP-Cron checks happen on page loads (or system cron ticks). There may be a 1-2 minute variance.
Solution: For exact timing, use a system cron job hitting wp-cron.php every minute.
Hourly Tasks Running Every Minute
Symptom: An hourly task runs every time the cron fires.
Cause: The isDue() check may not have the correct interval constraints.
Solution: Ensure you're chaining the frequency method:
// Good: Correct
$schedule->call($fn)->hourly()->name('my-task');
// Bad: Missing frequency — defaults to hourly but isDue() may always return true
$schedule->call($fn)->name('my-task');
Overlap Issues
Task Runs Twice Simultaneously
Symptom: A long-running task overlaps with itself.
Solution: Enable overlap prevention:
$schedule->call($fn)
->everyFiveMinutes()
->withoutOverlapping()
->name('long-task');
Task Never Runs After a Crash
Symptom: After a PHP fatal error during task execution, the task never runs again.
Cause: The lock was acquired but never released (task crashed before finally block or transient was set but process died).
Solution 1: Set a shorter lock expiration:
$schedule->call($fn)
->everyFiveMinutes()
->withoutOverlapping(30) // Lock expires after 30 minutes
->name('crashable-task');
Solution 2: Manually clear the lock:
// Clear the stuck lock
delete_transient('scheduler_lock_' . md5('crashable-task'));
Lock Key Issues
Two Tasks Sharing a Lock
Symptom: When one task runs, another unrelated task is blocked.
Cause: Both tasks have the same name.
Solution: Use unique, descriptive names:
$schedule->call($fn1)->name('plugin-a-cleanup');
$schedule->call($fn2)->name('plugin-b-cleanup');
WP-CLI Commands Not Running
WP_CLI Not Defined
Symptom: Command tasks do nothing.
Cause: WP_CLI constant is only defined when running via WP-CLI, not during web requests or cron.
Solution: WP-CLI commands are designed for WP-CLI context. For cron tasks, use closures or jobs instead:
// Good: Use a closure for cron-executed tasks
$schedule->call(function () {
wp_cache_flush();
})->hourly()->name('flush-cache');
// Warning: WP-CLI commands only work in WP-CLI context
$schedule->command('cache flush')->hourly()->name('cli-flush');
Job Dispatch Issues
Job Runs Synchronously
Symptom: Job tasks block the cron execution instead of being queued.
Cause: The wpzylos_dispatch_job filter isn't handled (queue package not installed).
Solution: Install and register the wpzylos-queue package, or accept synchronous execution.
Memory Issues
Cron Process Runs Out of Memory
Symptom: Allowed memory size exhausted during cron execution.
Solution 1: Increase memory limit for cron:
// wp-config.php
define('WP_MEMORY_LIMIT', '256M');
define('WP_MAX_MEMORY_LIMIT', '512M');
Solution 2: Break large tasks into smaller chunks:
$schedule->call(function () {
// Process in batches of 100
$items = get_pending_items(100);
foreach ($items as $item) {
process_item($item);
}
})->everyMinute()->name('batch-processor');
Debugging
Check Registered Tasks
$schedule = $app->make(Schedule::class);
foreach ($schedule->getTasks() as $task) {
error_log(sprintf(
'Task: %s | Type: %s | Interval: %ds | Due: %s',
$task->getName(),
$task->getType(),
$task->getIntervalSeconds(),
$task->isDue() ? 'YES' : 'NO'
));
}
Check Cron Events
# Using WP-CLI
wp cron event list
Check Active Locks
global $wpdb;
$locks = $wpdb->get_results(
"SELECT option_name, option_value FROM {$wpdb->options}
WHERE option_name LIKE '_transient_scheduler_lock_%'"
);
foreach ($locks as $lock) {
error_log("Lock: {$lock->option_name} = {$lock->option_value}");
}
Listen for Failures
add_action('wpzylos_scheduler_task_failed', function ($task, $e) {
error_log("[Scheduler FAIL] {$task->getName()}: {$e->getMessage()}");
error_log($e->getTraceAsString());
}, 10, 2);