laravel/framework · error · RuntimeException

To enable support for closure jobs, please install the illum

Error message

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

What it means

Thrown by Schedule::dispatchToQueue() (src/Illuminate/Console/Scheduling/Schedule.php:250) when a scheduled closure-based job is dispatched to the queue but CallQueuedClosure (from illuminate/queue) is not present. Closures must be serialized into a CallQueuedClosure object for queue transport.

Source

Thrown at src/Illuminate/Console/Scheduling/Schedule.php:250

        return $event;
    }

    /**
     * Dispatch the given job to the queue.
     *
     * @param  object  $job
     * @param  string|null  $queue
     * @param  string|null  $connection
     * @return void
     *
     * @throws \RuntimeException
     */
    protected function dispatchToQueue($job, $queue, $connection)
    {
        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);
        }

        if ($job instanceof ShouldBeUnique) {
            return $this->dispatchUniqueJobToQueue($job, $queue, $connection);
        }

        $this->getDispatcher()->dispatch(
            $job->onConnection($connection)->onQueue($queue)
        );
    }

    /**
     * Dispatch the given unique job to the queue.

View on GitHub (pinned to bd6b5437e6)

Solutions

  1. composer require illuminate/queue.
  2. If you don't need queueing, don't queue the closure — run it inline (remove onQueue).
  3. Convert the closure into a real job class implementing ShouldQueue; real classes don't require CallQueuedClosure.
  4. Re-run composer dump-autoload after installing.

Example fix

// before
$schedule->call(fn () => process())->everyMinute()->onQueue('default');
// illuminate/queue not installed

// after
// composer require illuminate/queue
$schedule->call(fn () => process())->everyMinute()->onQueue('default');
// or convert to a real job:
$schedule->job(ProcessJob::class)->everyMinute()->onQueue('default');
Defensive patterns

Strategy: validation

Validate before calling

if (! class_exists(\Illuminate\Queue\CallQueuedClosure::class)) {
    // don't queue closures; convert to a job class or install illuminate/queue
}

Type guard

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

Try / catch

try {
    $schedule->call(fn () => work())->onQueue('default');
} catch (\RuntimeException $e) {
    $schedule->call(fn () => work()); // run inline
}

Prevention

When it happens

Trigger: Schedule::call(fn () => ...)->onQueue(...) or Schedule::job(SomeJob) where the dispatched job ends up being a Closure, in a project where illuminate/queue is not installed (illuminate/bus-only or standalone illuminate/console setup).

Common situations: Standalone illuminate/console + illuminate/bus without illuminate/queue; partial Laravel subset for a CLI tool that tries to queue closures.

Related errors


AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06). Data as JSON: /data/errors/3245c3e9a97bea63.json. Report an issue: GitHub.