symfony/process · error · RuntimeException

Unable to kill the process

Error message

Unable to kill the process (%s).

What it means

On Windows, doSignal() kills via 'taskkill /F /T /PID'. If taskkill exits non-zero and the process still reports running, the kill failed and a RuntimeException with the taskkill output is thrown when $throwException is set.

Solutions

  1. Run the PHP process with sufficient privileges to terminate the target PID
  2. Retry the kill after a short delay and re-check isRunning()
  3. Catch RuntimeException and fall back to alternative termination (e.g. stop with a timeout, then SIGKILL path)

Example fix

// before
$process->stop(0, SIGKILL); // throws if taskkill fails
// after
try {
    $process->stop(0, SIGKILL);
} catch (RuntimeException $e) {
    // log $e->getMessage(); retry or escalate privileges
}
Defensive patterns

Strategy: try-catch

Validate before calling

if ( stripos(PHP_OS, 'WIN') === 0 && $process->isRunning()) { /* proceed with kill */ }

Try / catch

try { $process->stop(0, $sig); } catch (RuntimeException $e) { // taskkill failed
    sleep(1); if ($process->isRunning()) { /* escalate/retry */ }
}

Prevention

When it happens

Trigger: Calling signal()/stop() on Windows where taskkill fails due to insufficient privileges, an already-exiting PID, or antivirus interference, while isRunning() still returns true.

Common situations: Windows CI runners or dev machines killing long-running children, processes running under a different user/elevated context, PID reuse races.

Related errors


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

Appendix: source

Thrown at Process.php:1539

    {
        // Signal seems to be send when sigchild is enable, this allow blocking the signal correctly in this case
        if ($this->isSigchildEnabled() && \in_array($signal, $this->ignoredSignals)) {
            return false;
        }

        if (null === $pid = $this->getPid()) {
            if ($throwException) {
                throw new LogicException('Cannot send signal on a non running process.');
            }

            return false;
        }

        if ('\\' === \DIRECTORY_SEPARATOR) {
            exec(\sprintf('taskkill /F /T /PID %d 2>&1', $pid), $output, $exitCode);
            if ($exitCode && $this->isRunning()) {
                if ($throwException) {
                    throw new RuntimeException(\sprintf('Unable to kill the process (%s).', implode(' ', $output)));
                }

                return false;
            }
        } else {
            if (!$this->isSigchildEnabled()) {
                $ok = @proc_terminate($this->process, $signal);
            } elseif (\function_exists('posix_kill')) {
                $ok = @posix_kill($pid, $signal);
            } elseif ($ok = proc_open(\sprintf('kill -%d %d', $signal, $pid), [2 => ['pipe', 'w']], $pipes)) {
                $ok = false === fgets($pipes[2]);
            }
            if (!$ok) {
                if ($throwException) {
                    throw new RuntimeException(\sprintf('Error while sending signal "%s".', $signal));
                }

                return false;

View on GitHub (pinned to 99b85026db)