symfony/process · error · RuntimeException

Process is already running.

Error message

Process is already running.

What it means

A Process object represents at most one running process; calling start() while the previous instance is still running would leak the running process and corrupt the object's state, so start() throws RuntimeException if isRunning() is true.

Solutions

  1. Call $process->wait() (or stop()) before starting again, or check $process->isRunning().
  2. Create a new Process instance for each execution instead of reusing one object.
  3. Restructure concurrent launches into separate Process instances started in parallel.
  4. Guard start with if (!$process->isRunning()) { $process->start(); }.

Example fix

// before
$process->start();
$process->start(); // RuntimeException

// after
$process->start();
$process->wait();
$process->start(); // ok, previous run finished
Defensive patterns

Strategy: validation

Validate before calling

if ($process->isRunning()) {
    $process->wait(); // or stop()
}
$process->start();

Try / catch

try {
    $process->start();
} catch (\RuntimeException $e) {
    if ($e->getMessage() === 'Process is already running.') {
        $process->wait();
        $process->start();
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Calling $process->start() (directly or via run()) twice without waiting for the first run to finish — e.g. calling start() again after start() without stop()/wait(), or calling run() concurrently on the same instance.

Common situations: Loops that re-start the same Process object per item; retry logic that re-calls start on a still-running process; long-running commands combined with a second start call in request handling.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of symfony/process@99b85026db (2026-09-14). Data as JSON: /api/errors/626b57a59e6a1fed. Report an issue: GitHub.

Appendix: source

Thrown at Process.php:321

     * returns while the process runs in the background.
     *
     * The termination of the process can be awaited with wait().
     *
     * The callback receives the type of output (out or err) and some bytes from
     * the output in real-time while writing the standard input to the process.
     * It allows to have feedback from the independent process during execution.
     *
     * @param (callable('out'|'err', string):void)|null $callback A PHP callback to run whenever there is some
     *                                                            output available on STDOUT or STDERR
     * @param EnvArray                                  $env
     *
     * @throws ProcessStartFailedException When process can't be launched
     * @throws RuntimeException            When process is already running
     */
    public function start(?callable $callback = null, array $env = []): void
    {
        if ($this->isRunning()) {
            throw new RuntimeException('Process is already running.');
        }

        $this->resetProcessData();
        $this->starttime = $this->lastOutputTime = microtime(true);
        $this->callback = $this->buildCallback($callback);
        $descriptors = $this->getDescriptors(null !== $callback);

        if ($this->env) {
            $env += '\\' === \DIRECTORY_SEPARATOR ? array_diff_ukey($this->env, $env, 'strcasecmp') : $this->env;
        }

        $env += '\\' === \DIRECTORY_SEPARATOR ? array_diff_ukey($this->getDefaultEnv(), $env, 'strcasecmp') : $this->getDefaultEnv();

        if (\is_array($commandline = $this->commandline)) {
            $commandline = array_values(array_map(strval(...), $commandline));
        } else {
            $commandline = $this->replacePlaceholders($commandline, $env);
        }

View on GitHub (pinned to 99b85026db)