Laravel Tutorials

Laravel queue:work Now Prints Why the Worker Stopped

Published
Laravel queue:work Now Prints Why the Worker Stopped image

A queue:work process can exit for nine different reasons, and until Laravel 13.30 the console output looked the same for all of them. A worker running under Supervisor that restarts every few minutes might be hitting its memory limit, losing its database connection, or picking up a queue:restart from a deploy. Telling those apart meant registering an event listener or lining the restart up against a deploy log.

The command now writes the reason on its last line:

2026-09-01 13:20:40 Worker STOPPED Memory limit exceeded

Console and JSON Output

WorkCommand registers a listener for WorkerStopping next to the ones it already had for job and queue status, so the stop line comes out in the same two formats as the rest of the output. With --json, it is a record alongside the per-job lines:

{"level":"info","status":"stopped","reason":"empty","exit_code":0,"jobs_processed":12,"memory":34.0,"timestamp":"2026-09-01T13:20:40.118273+00:00"}

reason is the enum's backing value, which is stable to match on. memory is megabytes rounded to one decimal, or null when the worker recorded no figure. level is info for exit code 0 and warning for anything else.

The line is skipped under --quiet and --silent, and when the reason is null. A worker killed from outside, by an OOM killer or a SIGKILL from somewhere else, never runs the code that dispatches the event, so those exits stay silent.

Stop Reasons and Exit Codes

WorkerStopReason is an existing enum. This change added a description() method to it, so the console output and any listener you write read the same strings:

Value Description Exit code
empty Queue empty 0
empty_for Queue empty for the configured duration 0
max_jobs Maximum jobs exceeded 0
max_time Maximum run time exceeded 0
restart_signal Received restart signal 0
interrupted Interrupted 0
lost_connection Lost connection 0
memory Memory limit exceeded 12
timed_out Job timed out 1

Filtering the JSON Output

Once the reason is a field, finding it in a log is one filter:

php artisan queue:work --json --max-time=3600 2>&1 | \
    jq -c 'select(.status == "stopped")'

Under a process manager writing to a file, the same query runs against the log after the fact.

The WorkerStopping Event

The event is still the place to go for anything beyond reading output. It carries the reason along with the exit status, the number of jobs processed, the timestamp of the last job, and the memory in use when the worker quit:

use Illuminate\Queue\Events\WorkerStopping;

Event::listen(function (WorkerStopping $event) {
    Log::info('Worker stopped', [
        'reason' => $event->reason?->value,
        'status' => $event->status,
        'jobs' => $event->jobsProcessed,
        'memory' => $event->memoryUsage,
    ]);
});

Use it to count exits per reason for a dashboard, or to push each one into a metrics backend.

Contributed by Jack Bayliss in #61339.

Paul Redmond photo

Staff writer at Laravel News. Full stack web developer and author.

Sponsored

laravelcloud logo
Laravel Cloud

Easily create and manage your servers and deploy your Laravel applications in seconds.

Visit Laravel Cloud

The latest

View all →
Live Props for Real-Time Updates in Inertia.js v3.11 image

Live Props for Real-Time Updates in Inertia.js v3.11

Read article
Cooldown Dependency Policy now in Composer 2.11 image

Cooldown Dependency Policy now in Composer 2.11

Read article
Email Sending and Testing for Laravel in One Account image

Email Sending and Testing for Laravel in One Account

Read article
Decide with Jev: Tie Every AI Check to the Exact Draft image

Decide with Jev: Tie Every AI Check to the Exact Draft

Read article
PIE 1.5 Changes PHP Extension Installs in CI image

PIE 1.5 Changes PHP Extension Installs in CI

Read article
Delete Expired Sessions in Laravel With a Scheduled Command image

Delete Expired Sessions in Laravel With a Scheduled Command

Read article