laravel/framework · error · RuntimeException

To enable support for closure jobs, please install the…

Error message

To enable support for closure jobs, please install the illuminate/queue package.

What it means

Queueable::serializeJob is invoked when a Queueable object chains or pushes a closure as part of its job payload. Closures cannot be serialized to PHP strings directly; Laravel requires the illuminate/queue package which provides CallQueuedClosure, a wrapper that serializes the closure via Opis/Closure. Without illuminate/queue installed, no closure-based job can be queued.

Solutions

  1. Require illuminate/queue in composer.json (composer require illuminate/queue) matching the installed illuminate/bus version.
  2. Avoid dispatching closures; convert the closure to a named job class implementing ShouldQueue, which needs no CallQueuedClosure.
  3. If you intentionally exclude illuminate/queue, ensure no code path calls Queueable::serializeJob with a Closure (audit ->chain() and dispatch() callers).

Example fix

// before (closure job, illuminate/queue missing)
dispatch(function () use ($order) {
    Mail::to($order->user)->send(new OrderConfirmation($order));
});

// after
// composer require illuminate/queue
// OR convert to a named job:
class SendOrderConfirmation implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    public function __construct(public Order $order) {}
    public function handle(): void
    {
        Mail::to($this->order->user)->send(new OrderConfirmation($this->order));
    }
}
dispatch(new SendOrderConfirmation($order));
Defensive patterns

Strategy: validation

Validate before calling

// Validate closure-job support before dispatching closures
if (! class_exists(\Illuminate\Queue\CallQueuedClosure::class)) {
    throw new \RuntimeException('illuminate/queue is required to dispatch closure jobs.');
}
dispatch(fn () => Mail::to($user)->send(new WelcomeMail));

Type guard

function closureJobsAreSupported(): bool
{
    return class_exists(\Illuminate\Queue\CallQueuedClosure::class);
}

Prevention

When it happens

Trigger: Calling dispatch(function () { ... }) (closure dispatch) or chaining ->chain([fn () => ...]) from a Queueable job while illuminate/queue is not installed. Most common when consuming illuminate/bus or illuminate/database standalone without the full framework.

Common situations: Using illuminate/bus as a standalone dependency in a non-Laravel project. Splitting a Laravel monolith into a micro-service that pulls only part of illuminate/* and forgets illuminate/queue. Composer replace/exclude on illuminate/queue in a custom distribution.

Related errors


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

Appendix: source

Thrown at src/Illuminate/Bus/Queueable.php:321

            $this->chained = array_merge($this->chained, [$this->serializeJob($job)]);
        }

        return $this;
    }

    /**
     * Serialize a job for queuing.
     *
     * @param  mixed  $job
     * @return string
     *
     * @throws \RuntimeException
     */
    protected function serializeJob($job)
    {
        if ($job instanceof Closure) {
            if (! class_exists(CallQueuedClosure::class)) {
                throw new RuntimeException(
                    'To enable support for closure jobs, please install the illuminate/queue package.'
                );
            }

            $job = CallQueuedClosure::create($job);
        }

        return serialize($job);
    }

    /**
     * Dispatch the next job on the chain.
     *
     * @return void
     */
    public function dispatchNextJobInChain()
    {
        if (is_array($this->chained) && ! empty($this->chained)) {

View on GitHub (pinned to e0f6eb3518)