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

  1. Inspect the child's errorOutput in the exception message for the underlying cause (it usually contains the real stack trace).
  2. Add try/catch inside each task closure so failures are contained and reported via the result, not the process exit.
  3. Ensure closures only capture serializable values and that the app bootstraps in the child (correct base_path, env).
  4. 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

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


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