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

serpapi logo
SerpApi

The Web Search API for Your LLM and AI Applications

Visit SerpApi

The latest

View all →
The Laracon Archive image

The Laracon Archive

Read article
Group Adjacent Collection Items in Laravel with chunkBy() image

Group Adjacent Collection Items in Laravel with chunkBy()

Read article
Sidecar Brings Statamic's Control Panel to Your Existing Markdown Sites image

Sidecar Brings Statamic's Control Panel to Your Existing Markdown Sites

Read article
Collections chunkBy() and Storage Path Hardening in Laravel 13.30 image

Collections chunkBy() and Storage Path Hardening in Laravel 13.30

Read article
Forte: Parse and Rewrite Laravel Blade Templates image

Forte: Parse and Rewrite Laravel Blade Templates

Read article
Compoships: Eloquent Relationships on Multiple Columns image

Compoships: Eloquent Relationships on Multiple Columns

Read article