laravel/framework · error · RuntimeException
Scheduled closures can not be run in the background.
Error message
Scheduled closures can not be run in the background.
What it means
Thrown by CallbackEvent::runInBackground() (src/Illuminate/Console/Scheduling/CallbackEvent.php:104) unconditionally. Closures/anonymous callbacks cannot be serialized to a background process command string, so Laravel refuses to background them.
Source
Thrown at src/Illuminate/Console/Scheduling/CallbackEvent.php:104
* Determine if the event should skip because another process is overlapping.
*
* @return bool
*/
public function shouldSkipDueToOverlapping()
{
return $this->description && parent::shouldSkipDueToOverlapping();
}
/**
* Indicate that the callback should run in the background.
*
* @return void
*
* @throws \RuntimeException
*/
public function runInBackground()
{
throw new RuntimeException('Scheduled closures can not be run in the background.');
}
/**
* Run the callback.
*
* @param \Illuminate\Contracts\Container\Container $container
* @return int
*/
protected function execute($container)
{
try {
$this->result = is_object($this->callback)
? $container->call([$this->callback, '__invoke'], $this->parameters)
: $container->call($this->callback, $this->parameters);
return $this->result === false ? 1 : 0;
} catch (Throwable $e) {
$this->exception = $e;View on GitHub (pinned to bd6b5437e6)
Solutions
- Remove runInBackground() from closure-based events and let them run in the foreground.
- Move the logic into a real Artisan command and schedule it with Schedule::command('name') (a regular Event supports background).
- Dispatch the work to a queue job instead and schedule the job (still foreground at the schedule layer).
- If set at group level, split closures out of the grouped schedule.
Example fix
// before
$schedule->call(fn () => heavyWork())
->daily()
->runInBackground();
// after
$schedule->call(fn () => heavyWork())->daily();
// or convert to a command:
$schedule->command('reports:heavy')->daily()->runInBackground(); Defensive patterns
Strategy: validation
Validate before calling
if ($event instanceof \Illuminate\Console\Scheduling\CallbackEvent) {
// do NOT chain runInBackground(); run in foreground or convert to a command
} Type guard
function isClosureEvent($event): bool
{
return $event instanceof \Illuminate\Console\Scheduling\CallbackEvent;
} Try / catch
try {
$event->runInBackground();
} catch (\RuntimeException $e) {
// keep foreground execution
} Prevention
- Don't apply runInBackground() to closure/job events.
- For background work, schedule a real Artisan command via Schedule::command().
- Avoid setting runInBackground at the group() level when closures are inside.
When it happens
Trigger: Chaining ->runInBackground() on a Schedule::call(...) or Schedule::job(...) event (both produce CallbackEvent). Also when a scheduled closure inherits background behavior (e.g. inside a Schedule::group() that sets runInBackground) — the merge triggers the method on the closure event.
Common situations: Copy-pasting a command-style schedule (which supports background) onto a closure-based schedule, or applying runInBackground at the group level without realizing closures are included.
Related errors
- Invalid scheduled callback event. Must be a string or callab
- A scheduled event name is required to prevent overlapping. U
- A scheduled event name is required to only run on one server
- The seconds [$seconds] must be greater than zero.
- The seconds [$seconds] are not evenly divisible by 60.
AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06).
Data as JSON: /data/errors/8f9b0eb9bdb27901.json.
Report an issue: GitHub.