laravel/framework · error · Exception

Concurrent process failed with exit code [$result->exitCode(

Error message

Concurrent process failed with exit code [$result->exitCode()]. Message: $result->errorOutput()

What it means

Thrown by ProcessDriver::run() when any process in the pool exits with a failure status. The process driver serializes closures and runs them via the artisan invoke-serialized-closure command in a pool; if a child process crashes (non-zero exit code), the driver collects errorOutput() and rethrows as an Exception with the exit code and stderr.

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 bd6b5437e6)

Solutions

  1. Inspect the embedded errorOutput() in the message for the child's stderr/stack trace.
  2. Run the same closure synchronously (sync driver) to reproduce the underlying error.
  3. Ensure closures only capture serializable values and the artisan bootstrap is identical for workers.

Example fix

// before
$concurrency->run([
    fn () => $heavyService->process($job), // throws in child
]);

// after
$concurrency->driver('sync')->run([
    fn () => $heavyService->process($job), // reproduce locally first
]);
Defensive patterns

Strategy: fallback

Validate before calling

// reproduce synchronously first to validate the closure
try {
    app(\Illuminate\Concurrency\ConcurrencyManager::class)->driver('sync')->run($tasks);
} catch (\Throwable $e) {
    throw new \RuntimeException('Task fails in sync mode: '.$e->getMessage(), 0, $e);
}

Try / catch

try {
    $results = $concurrency->run($tasks);
} catch (\Exception $e) {
    // $e->getMessage() contains child exit code + errorOutput()
    $results = $concurrency->driver('sync')->run($tasks);
}

Prevention

When it happens

Trigger: Passing a closure that throws an exception, fatals, runs out of memory, or references unserializable/non-existent symbols. Also environment issues: missing artisan binary, wrong working directory, deserialization mismatch between parent and child PHP environments.

Common situations: Closure capturing an unserializable object. PHP version/extension mismatch between the dispatching process and the spawned workers. A closure that hits a fatal error (class not found, permission denied) inside the child.

Related errors


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