{"record":{"id":"8d042b8115fc6eb9","repo":"symfony/process","slug":"process-must-be-terminated-before-calling-s","errorCode":null,"errorMessage":"Process must be terminated before calling \"%s()\".","messagePattern":"Process must be terminated before calling \"(.+?)\\(\\)\"\\.","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"Process.php","lineNumber":1670,"sourceCode":"     *\n     * @throws LogicException if the process has not run\n     */\n    private function requireProcessIsStarted(string $functionName): void\n    {\n        if (!$this->isStarted()) {\n            throw new LogicException(\\sprintf('Process must be started before calling \"%s()\".', $functionName));\n        }\n    }\n\n    /**\n     * Ensures the process is terminated, throws a LogicException if the process has a status different than \"terminated\".\n     *\n     * @throws LogicException if the process is not yet terminated\n     */\n    private function requireProcessIsTerminated(string $functionName): void\n    {\n        if (!$this->isTerminated()) {\n            throw new LogicException(\\sprintf('Process must be terminated before calling \"%s()\".', $functionName));\n        }\n    }\n\n    /**\n     * Escapes a string to be used as a shell argument.\n     */\n    private function escapeArgument(?string $argument): string\n    {\n        if ('' === $argument || null === $argument) {\n            return '\"\"';\n        }\n        if ('\\\\' !== \\DIRECTORY_SEPARATOR) {\n            return \"'\".str_replace(\"'\", \"'\\\\''\", $argument).\"'\";\n        }\n        if (str_contains($argument, \"\\0\")) {\n            $argument = str_replace(\"\\0\", '?', $argument);\n        }\n        if (!preg_match('/[()%!^\"<>&|\\s[\\]=;*?\\'$]/', $argument)) {","sourceCodeStart":1652,"sourceCodeEnd":1688,"githubUrl":"https://github.com/symfony/process/blob/99b85026db14a68f02c6f3eeb01a1170ca25c491/Process.php#L1652-L1688","documentation":"requireProcessIsTerminated() guards post-mortem accessors (hasBeenSignaled, getTermSignal, hasBeenStopped, getStopSignal). These reflect final exit data that only exists after the process terminated; calling them earlier throws a LogicException.","triggerScenarios":"Calling $process->getTermSignal() or hasBeenSignaled() while the process is still running, e.g. inside progress polling before wait() returned.","commonSituations":"Checking exit signal information in a loop before completion, calling signal accessors right after start(), integrating with callbacks that run before termination.","solutions":["Call wait() and ensure the process finished before reading signal info","Check isTerminated() before calling these accessors","Use callbacks (e.g. wait's callable) that run only at termination for final reporting"],"exampleFix":"// before\n$process->start();\n$sig = $process->getTermSignal();\n// after\n$process->run();\nif ($process->isTerminated()) {\n    $sig = $process->hasBeenSignaled() ? $process->getTermSignal() : null;\n}","handlingStrategy":"validation","validationCode":"if ($process->isTerminated()) {\n    $signaled = $process->hasBeenSignaled();\n    $termSignal = $signaled ? $process->getTermSignal() : null;\n}","typeGuard":"function exitSignal(Symfony\\Component\\Process\\Process $p): ?int { return $p->isTerminated() && $p->hasBeenSignaled() ? $p->getTermSignal() : null; }","tryCatchPattern":"try { $sig = $process->getTermSignal(); } catch (LogicException $e) { $sig = null; }","preventionTips":["Only read exit/signal data after wait() or run() returns","Use isTerminated() as a gate for all post-mortem accessors","Collect final status in the wait() callback which runs after termination"],"tags":["php","process","lifecycle","not-terminated"],"backgroundTag":"invalid-state-transition","analyzedSha":"99b85026db14a68f02c6f3eeb01a1170ca25c491","analyzedAt":"2026-09-14T11:23:33.683Z","contentChangedAt":"2026-09-14T11:23:33.683Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}