The Laravel team released v13.34.0 with an opt-in way to count worker crashes toward a job's maxExceptions, a timeout signal for interruptible jobs, a duration property on the JobProcessed event, a --queue option for queue:flush, and more.
Count Worker Crashes Toward maxExceptions
William Julian-Vicary contributed a #[CountCrashesAsExceptions] attribute for queued jobs. A job that throws an exception counts toward $maxExceptions, but a job whose worker dies mid-run (an out-of-memory kill, a segfault, or a container being stopped) does not. A job that uses retryUntil() with a rate limiter instead of $tries can keep crashing workers until its deadline passes.
With the attribute, a crashed attempt counts as one exception:
use Illuminate\Queue\Attributes\CountCrashesAsExceptions;use Illuminate\Queue\Attributes\MaxExceptions; #[MaxExceptions(3), CountCrashesAsExceptions]class ProcessPodcast implements ShouldQueue{ public function retryUntil(): DateTime { return now()->addHour(); }}
A public $countCrashesAsExceptions = true; property works the same way. The worker writes a cache key when it starts an opted-in job and removes it when the attempt ends. If the key is still there on the next attempt, the previous one died, and the worker counts it against maxExceptions. The job needs a maxExceptions value, and each attempt adds two cache calls, which is why the feature is opt-in.
See #61737 for more details.
Interruptible Jobs Are Notified Before a Timeout
Jack Bayliss updated the worker so a job that implements the Interruptible contract receives SIGALRM when it hits its timeout. Before this release, the worker passed SIGTERM and similar signals to interruptible jobs, but a timeout killed the worker without calling the job first. The job can now save its progress before it is marked as failed:
use Illuminate\Contracts\Queue\Interruptible; class GenerateReport implements ShouldQueue, Interruptible{ public function interrupted(int $signal): void { if ($signal === SIGALRM) { $this->saveProgress(); } }}
The worker also dispatches the JobInterrupted event added in Laravel 13.31, and reports any exception thrown by interrupted() without blocking the timeout.
See #61651 for more details.
Job Duration on JobProcessed
Jack Bayliss also added a duration property to the JobProcessed event, in milliseconds. Before, you had to listen for JobProcessing and JobProcessed and track the start time yourself:
use Illuminate\Queue\Events\JobProcessed; Event::listen(function (JobProcessed $event) { Log::info("{$event->job->resolveName()} took {$event->duration}ms");});
The value is a float rounded to two decimal places. It is null when the event is dispatched from somewhere other than the worker.
See #61672 for more details.
Flush Failed Jobs for One Queue
queue:flush deletes every failed job on every queue. Jack Bayliss added a --queue option, so after a third-party API outage you can clear the failures from the affected queue and keep the rest:
php artisan queue:flush --queue=sftp-sync # Combine with --hours to keep recent failuresphp artisan queue:flush --queue=sftp-sync --hours=48
See #61630 for more details.
Schema::getColumn()
Francescu Garoby added Schema::getColumn(), which returns the details for one column instead of the full list from getColumns():
$column = Schema::getColumn('users', 'email'); $column['type_name']; // "varchar"$column['nullable']; // false
It returns an empty array when the column does not exist.
See #61759 for more details.
assertDatabaseCount() Accepts an Array
Jack Bayliss updated assertDatabaseCount() to accept an array of table and count pairs, so you can check several tables in one call:
$this->assertDatabaseCount([ Person::class => 1, Ticket::class => 2, 'movies' => 10,]);
See #61727 for more details.
Enums in Environment Checks
Dennis Petersmann updated App::environment() and the scheduler's environments() method to accept enums. Passing an enum to App::environment() threw an error, and passing one to environments() on a scheduled task caused the task to never run:
enum Environment: string{ case Staging = 'staging'; case Production = 'production';} App::environment(Environment::Production); Schedule::command('reports:send') ->daily() ->environments(Environment::Production);
The @env Blade directive compiles to App::environment(), so it accepts enums too. In a separate PR, Jack Bayliss updated Queue::isPaused() to accept enums, matching Queue::pause() and Queue::resume() (#61725).
See #61730 for more details.
requireMorphMap() Applies on Read
Pauline Vos extended Relation::requireMorphMap() to the read side of polymorphic relations. With it turned on, Laravel already threw a ClassMorphViolationException when saving a model with no morph map entry. When loading a morphTo relation, an unmapped *_type value was still used as a class name. Laravel now throws the same exception when a *_type value is not in the morph map, for both lazy and eager loading.
Applications that do not call requireMorphMap() are not affected.
See #61711 for more details.
Release Notes
You can see the complete list of new features and updates below and the diff between 13.33.0 and 13.34.0 on GitHub. The following release notes are directly from the changelog:
v13.34.0
- [13.x] Include duration in JobProcessed event by @jackbayliss in #61672
- [13.x] Notify job when a timeout is going to occur by @jackbayliss in #61651
- [13.x] Add --queue option to queue:flush by @jackbayliss in #61630
- Allow null in docblocks to match existing code by @miclf in #61679
- [13.x] Support LazyCollection values in between clauses by @vikas-kushwaha-dev in #61678
- [13.x] Sync refreshed attributes to original state after increment / decrement by @ilhammmaulana in #61680
- [13.x] Fix
Number::parseInt()returning false for values above 32-bit range by @sumaiazaman in #61691 - [13.x] Fix
PhpRedisLock::refresh()when phpredis serialization is enabled by @sumaiazaman in #61690 - [13.x] Fix Collection::mode() returning values not present in the data when nulls exist by @MoZayedSaeid in #61686
- [13.x] Adjust some worker docblocks by @jackbayliss in #61683
- [13.x] Respect the relation's owner key in whereMorphedTo / whereNotMorphedTo by @axlon in #61712
- Enforce morph map on read side of polymorphic relations by @paulinevos in #61711
- [13.x] Display error in
queue:restartwhen worker isn't restartable by @jackbayliss in #61706 - [13.x] Test Improvements by @crynobone in #61697
- [13.x] Throw ValueError in LazyCollection::combine() on mismatched lengths by @mrjavadseydi in #61696
- [13.x] Fix
trans_choice()picking the wrong plural form for negative numbers by @sumaiazaman in #61700 - [13.x] Don't swallow deadlocks in
DatabaseLockinside a transaction by @lazerg in #61708 - [13.x] Fix
sortBy()with multiple columns andSORT_NUMERICtruncating fractional values by @sumaiazaman in #61699 - [13.x] Fix Number::format(), percentage() and currency() returning "-0" by @mrjavadseydi in #61716
- [13.x] Allow assertDatabaseCount to accept an array by @jackbayliss in #61727
- [13.x] Fix multipart requests losing attached stream contents on retry by @mehdishakki in #61728
- [13.x] Allow enums in Queue::isPaused() by @jackbayliss in #61725
- [13.x] Fix double negation in Eloquent
whereNot()with array conditions by @Nejcc in #61721 - [13.x] Allow enums in environment checks by @Taldres in #61730
- [13.x] Return an empty collection from
shift($count)on an empty collection by @Nejcc in #61723 - [13.x] Test with real objects by @jasonmccreary in #61663
- [13.x] Cloud exceptions by @timacdonald in #61704
- [13.x] Run workflows on Windows 2025 by @jnoordsij in #61718
- [13.x] Use constant time lookups for validator exclusions by @owenconti in #61698
- [13.x] Preserve existing PDO options when forcing emulated prepares for pooled Cloud Postgres by @laurenschristian in #61735
- [13.x] Fix using elsePushIf blade directive with complex conditions by @mrjavadseydi in #61741
- [13.x] Fix Memcached locks longer than 30 days expiring immediately by @sumaiazaman in #61742
- [13.x] Allow jobs to count worker crashes towards maxExceptions by @williamjulianvicary in #61737
- [13.x] Fix Redis throttle returning a timestamp as the remaining attempts by @sumaiazaman in #61748
- Fix typos for Markdown files by @peter279k in #61746
- [13.x] Support named S3 credential providers and Cloud IAM disks by @kieranbrown in #61676
- [13.x] Supports
phpstan/phpstan:2.2.15by @crynobone in #61719 - [13.x] feat: add the new 'Database\Schema\Builder::getColumn' method by @fgaroby in #61759
- Apply fixes from StyleCI by @taylorotwell in #61763
- [13.x] Fix through() returning HasOneThrough for MorphMany relations by @mrjavadseydi in #61760
- [13.x] Exclude refreshed attributes when replicating models by @ilhammmaulana in #61751
- [13.x] Fix
wherePivotBetween()being ignored bysync(),detach()andupdateExistingPivot()by @sumaiazaman in #61747 - [13.x] Fix flaky scheduling test on Windows by @jasonmccreary in #61768
- [13.x] Fix findOrFail() with an array of enum ids by @jabysa in #61765
- [13.x] Fix
Builder::with()when a null callback is passed explicitly by @lazerg in #61769 - Final Mockery cleanup by @jasonmccreary in #61739
- [13.x] Treat dotted keys literally in ArrayStore::touch() by @Button99 in #61777
- [13.x] Fix memoized tagged cache
many()returning null for numeric keys by @sajjadhossainshohag in #61780 - [13.x] Add regression test for createPayloadUsing on the queue manager by @fajarwz in #61773
- [13.x] Add mime type to the files reportable details by @sbourouis in #61772
- [13.x] Explicitly enable imagick extension for CI tests by @jnoordsij in #61756