{"record":{"id":"6db09d7f871efd32","repo":"symfony/process","slug":"pass-the-callback-to-the-process-start-method-or-call-6db09d","errorCode":null,"errorMessage":"Pass the callback to the \"Process::start\" method or call enableOutput to use a callback with \"Process::waitUntil\".","messagePattern":"Pass the callback to the \"Process::start\" method or call enableOutput to use a callback with \"Process::waitUntil\"\\.","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"Process.php","lineNumber":524,"sourceCode":"     * It allows to have feedback from the independent process during execution.\n     *\n     * @param-immediately-invoked-callable $callback\n     *\n     * @param (callable('out'|'err', string):bool)|null $callback A PHP callback to run whenever there is some\n     *                                                            output available on STDOUT or STDERR\n     *\n     * @throws RuntimeException         When process timed out\n     * @throws LogicException           When process is not yet started\n     * @throws ProcessTimedOutException In case the timeout was reached\n     */\n    public function waitUntil(callable $callback): bool\n    {\n        $this->requireProcessIsStarted(__FUNCTION__);\n        $this->updateStatus(false);\n\n        if (!$this->processPipes->haveReadSupport()) {\n            $this->stop(0);\n            throw new LogicException('Pass the callback to the \"Process::start\" method or call enableOutput to use a callback with \"Process::waitUntil\".');\n        }\n        $callback = $this->buildCallback($callback);\n\n        $ready = false;\n        while (true) {\n            $this->checkTimeout();\n            $running = '\\\\' === \\DIRECTORY_SEPARATOR ? $this->isRunning() : $this->processPipes->areOpen();\n            $output = $this->processPipes->readAndWrite($running, '\\\\' !== \\DIRECTORY_SEPARATOR || !$running);\n\n            foreach ($output as $type => $data) {\n                if (3 !== $type) {\n                    $ready = $callback(self::STDOUT === $type ? self::OUT : self::ERR, $data) || $ready;\n                } elseif (!isset($this->fallbackStatus['signaled'])) {\n                    $this->fallbackStatus['exitcode'] = (int) $data;\n                }\n            }\n            if ($ready) {\n                return true;","sourceCodeStart":506,"sourceCodeEnd":542,"githubUrl":"https://github.com/symfony/process/blob/99b85026db14a68f02c6f3eeb01a1170ca25c491/Process.php#L506-L542","documentation":"waitUntil($callback) polls the running process until the callback returns true, which requires read support on the pipes. If start() was called without a callback and output was never enabled, waitUntil cannot read output for its callback and throws LogicException (stopping the process first), mirroring the wait() restriction.","triggerScenarios":"Calling $process->waitUntil($fn) after start() with no callback and without enableOutput()/enableOutputByDefault(); haveReadSupport() returns false so the exception is raised before any polling.","commonSituations":"Condition-based waiting (e.g. 'wait until the server prints Ready') on a process started without output collection; migrating from wait() code that had read support to waitUntil() on a start()-without-callback flow.","solutions":["Call $process->enableOutput() before start() (or Process::enableOutputByDefault() globally), then start()/waitUntil().","Pass a callback to start() to implicitly enable read support, then use waitUntil().","If output polling is not needed, replace waitUntil with wait() plus a status check on completion.","Ensure requireProcessIsStarted passes too — waitUntil only works on a started process."],"exampleFix":"// before\n$process->start();\n$process->waitUntil(fn ($type, $buffer) => str_contains($buffer, 'Ready')); // LogicException\n\n// after\n$process->enableOutput();\n$process->start();\n$process->waitUntil(fn ($type, $buffer) => str_contains($buffer, 'Ready'));","handlingStrategy":"validation","validationCode":"$process->enableOutput();\n$process->start();\n// now safe:\n$process->waitUntil(fn (string $type, string $buffer): bool => str_contains($buffer, 'Ready'));","typeGuard":null,"tryCatchPattern":"try {\n    $process->waitUntil($predicate);\n} catch (\\LogicException $e) {\n    if (str_contains($e->getMessage(), '\"Process::waitUntil\"')) {\n        // restructure: enableOutput() before start, or start($callback)\n    }\n    throw $e;\n}","preventionTips":["Whenever using waitUntil for output-based conditions, always enable output before start().","Remember waitUntil also requires the process to be started (requireProcessIsStarted).","Centralize process startup in a helper that enables output and passes a callback so wait/waitUntil are always legal.","On this error the process is stopped (0s) — restart it when recovering."],"tags":["php","symfony-process","callback","wait","api-misuse"],"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"}