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
Queueable::serializeJob() wraps a Closure into CallQueuedClosure before serializing it into a job chain. If the Illuminate\Queue\CallQueuedClosure class does not exist (the illuminate/queue package is not installed), it throws a RuntimeException telling the user to install it. Closure chaining requires the queue subsystem.
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 bd6b5437e6)
Solutions
- Run 'composer require illuminate/queue' to provide CallQueuedClosure and the queue runtime.
- Alternatively, replace the closure with a concrete job class so no CallQueuedClosure wrapping is needed.
- If closures must be used, also install laravel/serializable-closure (a dependency used by CallQueuedClosure).
Example fix
// before
$job->chain([
function () { logger('done'); },
]);
// after (option A: install package)
// composer require illuminate/queue
// after (option B: use a class)
class LogDoneJob implements ShouldQueue {
use Queueable;
public function handle() { logger('done'); }
}
$job->chain([new LogDoneJob()]); Defensive patterns
Strategy: validation
Validate before calling
if (! class_exists(\Illuminate\Queue\CallQueuedClosure::class)) {
throw new \RuntimeException('Install illuminate/queue to chain closure jobs.');
}
$job->chain([$closureJob]); Type guard
function closureJobsAreSupported(): bool
{
return class_exists(\Illuminate\Queue\CallQueuedClosure::class)
&& class_exists(\Laravel\SerializableClosure\SerializableClosure::class);
} Try / catch
try {
$job->chain([fn () => doWork()]);
} catch (\RuntimeException $e) {
if (str_contains($e->getMessage(), 'illuminate/queue')) {
// run composer require illuminate/queue then retry
}
throw $e;
} Prevention
- Prefer concrete job classes over closures for testability.
- Pin illuminate/queue in composer.json when using illuminate/bus.
- Document package dependencies for closure-based jobs.
When it happens
Trigger: Calling $job->chain([function () { ... }]) or appendToChain(function () {...}) in an installation that has illuminate/bus but not illuminate/queue. Serializing a closure-based job via the Queueable trait without the queue package present.
Common situations: Using illuminate/bus or illuminate/pipeline standalone packages and trying to chain closures. A partial composer require that pulled bus but not queue. Refactoring a monolith into a slim worker that omits illuminate/queue.
Related errors
- Queue resolver did not return a Queue implementation.
- Attempted to batch job [%s], but it does not use the Batchab
- Unable to bind custom driver callback
- The failed job provider does not have a configured queue
- Managed queue [{$name}] does not exist.
AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06).
Data as JSON: /data/errors/bdd41637b27643bb.json.
Report an issue: GitHub.