How to fix Laravel Queue's "ModelNotFoundException"
What to do when your Laravel queued job fails due to the relevant models not being found? Let's examine your options of handling ModelNotFoundException in a queued job.
Illuminate\Database\Eloquent\ModelNotFoundException: No query results for model [App\Models\User]
This is a very common error, especially on larger scale projects when lots of jobs are being processed and unexpected things happens. There's a couple main reasons why this can happen:
- You did not save the model to the database before dispatching the job
- The model was deleted between job dispatch and job execution
Luckily both reasons are easy to fix.
Saving models before job dispatch
Make sure you understand the difference between Model::create()
, Model::make()
, and new Model
in your application.
$model = new Model
creates a new model instance, but it still hasn't saved it to the database. Once you're finished assigning properties, make sure to call the ->save()
method to save it to the database.
<?php $user = new User; $user->name = 'Arunas'; // At this point, the model is not saved yet. // Make sure to call the ->save() method to save it to the database $user->save();
The same applies when creating instances through Model::make()
:
<?php $user = User::make(['name' => 'Arunas']); // at this point, the model is not saved yet. // Make sure to call the ->save() method to save it to the database $user->save();
Model was deleted between job dispatch and job execution
Unless your queue driver is set to sync
, your queued jobs will be executed at some point in the future. A lot can happen in that time, even if it's just one second. A database transaction rollback, an exception, or something else that would cause your model to get deleted. And by the time your queue worker picks up the job, the model is all gone and the job fails with a ModelNotFoundException.
In that case, you have two options to handle this.
a. Ignore the job when the models are missing
This can often be the desired behaviour. If, for example, you have a job for sending a welcome email to a new user, but the user registration fails - you don't want that email to be sent.
You can achieve this by adding a public $deleteWhenMissingModels = true;
property to the job (Laravel 5.7+):
<?php class SendWelcomeEmail implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; /** * @var User */ public $user; /** * Delete the job if its models no longer exist. * * @var bool */ public $deleteWhenMissingModels = true; ... }
b. Don't serialize models and store whole instances on the worker queue
This way, the whole object is stored as-is in the worker queue. When the job is picked up by the worker, it doesn't need to load the model from the database any more because it already has it!
To do this, simply remove the SeralizesModels
trait from the job:
<?php class SendWelcomeEmail implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable; /** * @var User */ public $user; ... }