laravel/framework · error · EntityNotFoundException

Queueable entity [{$type}] not found for ID [{$id}].

Error message

Queueable entity [{$type}] not found for ID [{$id}].

What it means

QueueEntityResolver.resolve() is used when a queued job serializes an Eloquent model by reference (ModelIdentifier) and Laravel re-fetches it on unserialization. If (new $type)->find($id) returns null - the row was deleted, the ID is wrong, or the model class changed - it throws EntityNotFoundException with the type and id.

Source

Thrown at src/Illuminate/Database/Eloquent/QueueEntityResolver.php:27

{
    /**
     * Resolve the entity for the given ID.
     *
     * @param  string  $type
     * @param  mixed  $id
     * @return mixed
     *
     * @throws \Illuminate\Contracts\Queue\EntityNotFoundException
     */
    public function resolve($type, $id)
    {
        $instance = (new $type)->find($id);

        if ($instance) {
            return $instance;
        }

        throw new EntityNotFoundException($type, $id);
    }
}

View on GitHub (pinned to e0f6eb3518)

Solutions

  1. Catch \Illuminate\Contracts\Queue\EntityNotFoundException in the job's handle() and skip/retry/abort gracefully.
  2. Pass the scalar ID (or a DTO) into the job instead of the model and re-fetch with findOrFail inside handle() so you control the missing-row behavior.
  3. Use the SerializesModels trait (default) so the model is re-resolved; ensure the row still exists at runtime or handle its absence.
  4. For soft-deleted models, fetch with withTrashed() in the job.

Example fix

// before - dispatching the model
ProcessReport::dispatch($user);
// later, user row deleted -> unserialize throws

// after - pass ID, fetch in handle()
class ProcessReport implements ShouldQueue {
    public function __construct(public int $userId) {}
    public function handle() {
        $user = User::find($this->userId);
        throw_unless($user, new \RuntimeException('User missing'));
        // ...
    }
}
ProcessReport::dispatch($user->id);
Defensive patterns

Strategy: try-catch

Validate before calling

// Prefer passing scalar IDs into jobs and re-fetch inside handle()
// Validate existence at the boundary before dispatch:
if (! User::whereKey($userId)->exists()) {
    throw new \RuntimeException("Cannot dispatch: user {$userId} missing");
}
ProcessJob::dispatch($userId);

Type guard

function entityExists(string $type, mixed $id): bool {
    return (new $type)->whereKey($id)->exists();
}

Try / catch

try {
    // job body that resolves the model
    $user = User::findOrFail($id);
} catch (\Illuminate\Contracts\Queue\EntityNotFoundException $e) {
    // row gone between dispatch and run - skip or requeue
    $this->release(60); // or delete()
}

Prevention

When it happens

Trigger: Dispatching a queued job with a model in its constructor, then deleting that model's row before the worker picks the job up; passing an unserialized/soft-deleted model id; model class rename leaving $type pointing at a stale class.

Common situations: Race between a delete and a queued worker; long-running queues holding stale references; deleting then re-running a queue:work; CI using an in-memory DB across processes; soft-deleted models without withTrashed on resolution.

Related errors


AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11). Data as JSON: /api/errors/6be3dcdd113facf5. Report an issue: GitHub.