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 →
Live Stream: Building a Social Network in PHP in 48 Hours image

Live Stream: Building a Social Network in PHP in 48 Hours

Read article
Health for Laravel: Kubernetes Probes and Prometheus Metrics image

Health for Laravel: Kubernetes Probes and Prometheus Metrics

Read article
Fast Excel 5.x Adds Streaming Imports and Safer Exports image

Fast Excel 5.x Adds Streaming Imports and Safer Exports

Read article
What We Know About Laravel 14 image

What We Know About Laravel 14

Read article
Difflock: Lint Laravel Migrations and Diff Your Schema image

Difflock: Lint Laravel Migrations and Diff Your Schema

Read article
Fresh Package: Laravel Package Skeleton with Testbench, CI, and Boost Integration image

Fresh Package: Laravel Package Skeleton with Testbench, CI, and Boost Integration

Read article