laravel/framework · error · Exception
Concurrent process failed with exit code [%s]. Message: %s
Error message
Concurrent process failed with exit code [%s]. Message: %s
What it means
Thrown by ProcessDriver::run() when a concurrent subprocess in the pool exits with a failure ($result->failed()). The driver spawns child PHP processes (via the invoke-serialized-closure command) to run each task; if any child crashes or returns a non-zero exit code, its exitCode and errorOutput are wrapped in an Exception. The message includes the exit code and the child's stderr/stderr captured output.
Source
Thrown at src/Illuminate/Concurrency/ProcessDriver.php:53
$command = Application::formatCommandString('invoke-serialized-closure');
$results = $this->processFactory->pool(function (Pool $pool) use ($tasks, $command, $timeout) {
foreach (Arr::wrap($tasks) as $key => $task) {
$process = $pool->as($key)->path(base_path())->env([
'LARAVEL_INVOKABLE_CLOSURE' => base64_encode(
serialize(new SerializableClosure($task))
),
])->command($command);
if (! is_null($timeout)) {
$process->timeout($timeout);
}
}
})->start()->wait();
return $results->collect()->mapWithKeys(function ($result, $key) {
if ($result->failed()) {
throw new Exception('Concurrent process failed with exit code ['.$result->exitCode().']. Message: '.$result->errorOutput());
}
$output = $result->output();
if (($pos = strpos($output, "\x1f\x8b")) !== false) {
$output = substr($output, 0, $pos);
}
$result = json_decode($output, true);
if (! $result['successful']) {
throw new $result['exception'](
...(! empty(array_filter($result['parameters'], fn ($parameter) => ! is_null($parameter)))
? $result['parameters']
: [$result['message']])
);
}
View on GitHub (pinned to e0f6eb3518)
Solutions
- Inspect the child's errorOutput in the exception message for the underlying cause (it usually contains the real stack trace).
- Add try/catch inside each task closure so failures are contained and reported via the result, not the process exit.
- Ensure closures only capture serializable values and that the app bootstraps in the child (correct base_path, env).
- Increase the timeout if the task is being killed mid-run.
Example fix
// before
$results = Concurrency::run([
fn () => riskyOperation($input),
]);
// after
$results = Concurrency::run([
function () use ($input) {
try { return riskyOperation($input); }
catch (\Throwable $e) { return ['error' => $e->getMessage()]; }
},
]); Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-validate that closures capture only serializable values
$reflect = new \ReflectionFunction($task);
foreach ($reflect->getStaticVariables() as $v) {
try { serialize($v); } catch (\Throwable $e) {
throw new \InvalidArgumentException('Task captures unserializable value');
}
} Type guard
function isSerializableClosure(\Closure $c): bool {
try { serialize(new \Laravel\SerializableClosure\SerializableClosure($c)); return true; }
catch (\Throwable $e) { return false; }
} Try / catch
try {
$results = Concurrency::run($tasks);
} catch (\Exception $e) {
// $e->getMessage() contains the child exit code and errorOutput
logger()->error('Concurrency failed: '.$e->getMessage());
$results = array_map(fn ($t) => $t(), $tasks); // fallback to sync
} Prevention
- Wrap each task body in try/catch so failures return a result, not a crash.
- Capture only serializable values; avoid PDO/resources/singletons in closures.
- Set an explicit timeout sized to the slowest task.
- Log errorOutput from the exception to diagnose the child cause.
When it happens
Trigger: Passing a closure to Concurrency::run([...]) (process driver) where one closure throws, references an undefined symbol, or the child PHP process is killed (OOM, signal, timeout).
Common situations: A task closure throws an uncaught exception; the serialized closure captures an unserializable object; child process exceeds the timeout and is killed; missing env/bootstrap so the invoke-serialized-closure command fails; out-of-memory kills.
Related errors
- Due to PHP limitations, the fork driver may not be used with
- Please install the "spatie/fork" Composer package in order t
- Callback must be a callable, callback array, or a 'Class@met
- Auth guard [{$name}] is not defined.
- Auth driver [{$config['driver']}] for guard [{$name}] is not
AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11).
Data as JSON: /api/errors/708287dd73af6f2e.
Report an issue: GitHub.