Skip to content

[12.x]ScheduledTaskFailed not dispatched on scheduled task failing #55572

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

achrafAa
Copy link
Contributor

@achrafAa achrafAa commented Apr 27, 2025

Fix: ScheduledTaskFailed Event Not Dispatching on All Failures in Laravel Scheduler

Overview

Laravel's scheduler fails to dispatch the ScheduledTaskFailed event under various failure scenarios, despite documentation stating it should. This affects commands that throw exceptions, return non-zero exit codes, fail in the background, or fail at the system level.

This change ensures the ScheduledTaskFailed event is dispatched consistently for all failure conditions.

This update is non-breaking, as it doesn't interfere with other events or trigger any unintended behavior. It corrects the behavior of the ScheduledTaskFailed event, which was not being fired as expected. Essentially, this is a bug fix rather than a new feature. We don't anticipate any breaking changes, but if you have a different perspective or see a potential issue, we’d appreciate your input in the comments so we can address it accordingly.

Problem Description

The ScheduledTaskFailed event does not fire in the following situations:

  • Commands that throw exceptions
  • Commands that return non-zero exit codes
  • Background process failures
  • System-level command execution failures

Even though Laravel includes code for dispatching this event, it never actually fires under real-world usage, as noted in Issue #55352.

✅ Expected Behavior

The ScheduledTaskFailed event should be dispatched when any scheduled task fails.

❌ Actual Behavior

Failures are visible in the console, but the ScheduledTaskFailed event is never triggered.


How to Reproduce

1. Register a Failing Command

// routes/console.php
Artisan::command('failing-command', function () {
    throw new Exception('This should trigger ScheduledTaskFailed');
})->everyMinute();

2. Set Up an Event Listener

// App\Providers\AppServiceProvider.php
use Illuminate\Console\Events\ScheduledTaskFailed;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;

public function boot()
{
    Event::listen(ScheduledTaskFailed::class, function () {
        Log::error('This never executes');
    });
}

3. Run the Scheduler

php artisan schedule:run --verbose

Result:
The command fails visibly, but no event is dispatched.


Root Cause

Laravel’s ScheduleRunCommand::runEvent() It does not consistently dispatch ScheduledTaskFailed if a task fails silently with a non-zero exit code.
and the logic that checks for such conditions is missing.


Changes Made

  • Modified ScheduleRunCommand::runEvent() to:
    • Inspect the task's exit code after execution.
    • Dispatch ScheduledTaskFailed when a non-zero exit code is encountered.
  • Ensured existing exception-based dispatch remains intact.

Benefits

  • Reliable failure detection: All task failures are captured consistently.
  • Unified failure monitoring: Centralize logging, alerts, and recovery in one event listener.
  • Non-breaking: Keeps existing logic intact for success and exception cases.

Screenshots of testing

Actual code

case 1 : successful command :

image
Correct: No ScheduledTaskFailed event fired ✅

case 2 : failed command :

image
Bug: ScheduledTaskFailed not fired when command fails ❌

PR code

case 1 : successful command :

image
Correct: No ScheduledTaskFailed event fired ✅

case 2 : failed command :

image
Fixed: ScheduledTaskFailed now properly fires on failure ✅

Usage

After this fix, monitor all scheduled task failures like this:

use Illuminate\Console\Events\ScheduledTaskFailed;

Event::listen(ScheduledTaskFailed::class, function ($event) {
    Log::error('Scheduled task failed', [
        'command'  => $event->task->command,
        'exitCode' => $event->task->exitCode
    ]);

    // Optional: Trigger alert or recovery actions
});

Related


Status

✅ Fixed — ScheduledTaskFailed is now reliably dispatched for all failure types.

…tion test

- Updated `ScheduleRunCommand` to dispatch `ScheduledTaskFailed` event when a scheduled command fails.
- Added handling for exit codes to improve error reporting.
- Introduced `ScheduleRunCommandTest` to verify that failure events are dispatched correctly for failing scheduled tasks.
…tion test

- Updated `ScheduleRunCommand` to dispatch `ScheduledTaskFailed` event when a scheduled command fails.
- Added handling for exit codes to improve error reporting.
- Introduced `ScheduleRunCommandTest` to verify that failure events are dispatched correctly for failing scheduled tasks.
…tion test

- Updated `ScheduleRunCommand` to dispatch `ScheduledTaskFailed` event when a scheduled command fails.
- Added handling for exit codes to improve error reporting.
- Introduced `ScheduleRunCommandTest` to verify that failure events are dispatched correctly for failing scheduled tasks.
@erickcomp
Copy link
Contributor

Much needed bugfix!

@taylorotwell taylorotwell merged commit 1deaa36 into laravel:12.x Apr 29, 2025
58 checks passed
@tarkis
Copy link
Contributor

tarkis commented Apr 30, 2025

Am I the only one who started getting Schedule errors after this update?

Error:
[2025-04-30 06:15:00] local.ERROR: Scheduled command ['/usr/bin/php8.4' 'artisan' horizon:snapshot] failed with exit code []. {"exception":"[object] Exception(code: 0): Scheduled command ['/usr/bin/php8.4' 'artisan' horizon:snapshot] failed with exit code []. at var/www/html/vendor/laravel/framework/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php:201)

it looks like its related to running your commands in background. If you have a Schedule set up like this:
Schedule::command('horizon:snapshot')->everyFiveMinutes()->onOneServer()->runInBackground(); you will get an error. If you do not use runInBackground() then all is good.

@achrafAa
Copy link
Contributor Author

achrafAa commented Apr 30, 2025

@tarkis
You're encountering this error because of an important change in how Laravel now handles command exit codes in scheduled tasks. Let me break this down:

Root Cause:

The update introduced strict exit code checking for background commands. Your logs show an empty exit code ([]), which fails the strict comparison with 0 (success).

When using runInBackground(), the Horizon snapshot command isn't properly returning an exit code

Previously, this silent failure went unnoticed

The update properly surfaces these issues by validating exit codes

Immediate Fix (not optimal):

Schedule::command('horizon:snapshot')->everyFiveMinutes()->onOneServer();
// Remove ->runInBackground() temporarily

Proper Solution:
This should ultimately be addressed in the Horizon package by:

Ensuring the command returns proper exit codes (0 for success)

Adding explicit status code handling for background processes

I'd recommend:

Opening an issue with Horizon if none exists

Checking if your Horizon version needs updating

Monitoring your queue workers during this transition

The error is actually helping surface a real issue - your scheduled command wasn't properly succeeding even before the update. This change makes your system more reliable in the long run.

@tarkis
Copy link
Contributor

tarkis commented Apr 30, 2025

Removing runInBackground() is not a solution and all commands were returning 0 as success. problem was that exitCode was not set for tasks running in background. I have made a new PR that adds exitCode for all tasks #55605

@achrafAa
Copy link
Contributor Author

can you confirm that this change works with your horizon setup, or provide clear reproduction steps if this is a known issue

@tarkis
Copy link
Contributor

tarkis commented Apr 30, 2025

it not only horizon. You can reproduce with any command.

  1. Create a command php artisan make:command CommandExit
  2. Add that command to console.php route Schedule::command('app:command-exit')->everyTenSeconds();
  3. Run php artisan schedule:work - all is working
  4. Add updated command to console.php route Schedule::command('app:command-exit')->everyTenSeconds()->runInBackground();
  5. Run php artisan schedule:work - the one with runInBackground() starts throwing exceptions.

p.s. commands themselves are working and executing. The problem is that at the end runInBackground() throws an exception.

@achrafAa
Copy link
Contributor Author

@tarkis its fixed with your code changes, i recommend you add reproduce instructions in your PR for other contributors.

@GrahamCampbell GrahamCampbell changed the title 12.x scheduled task failed not dispatched on scheduled task failing [12.x] Scheduled task failed not dispatched on scheduled task failing Apr 30, 2025
@AhmedAlaa4611
Copy link
Contributor

Can you add tests for this to ensure that we will not break it in future updates?

@achrafAa achrafAa changed the title [12.x] Scheduled task failed not dispatched on scheduled task failing [12.x] 'ScheduledTaskFailed' not dispatched on scheduled task failing Apr 30, 2025
@achrafAa achrafAa changed the title [12.x] 'ScheduledTaskFailed' not dispatched on scheduled task failing [12.x]ScheduledTaskFailed not dispatched on scheduled task failing Apr 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants