{"record":{"id":"225343cf7ae2339f","repo":"symfony/process","slug":"pass-the-callback-to-the-process-start-method-or-call","errorCode":null,"errorMessage":"Pass the callback to the \"Process::start\" method or call enableOutput to use a callback with \"Process::wait\".","messagePattern":"Pass the callback to the \"Process::start\" method or call enableOutput to use a callback with \"Process::wait\"\\.","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"Process.php","lineNumber":478,"sourceCode":"     * @param (callable('out'|'err', string):void)|null $callback A PHP callback to run whenever there is some\n     *                                                            output available on STDOUT or STDERR\n     *\n     * @return int The exitcode of the process\n     *\n     * @throws ProcessTimedOutException When process timed out\n     * @throws ProcessSignaledException When process stopped after receiving signal\n     * @throws LogicException           When process is not yet started\n     */\n    public function wait(?callable $callback = null): int\n    {\n        $this->requireProcessIsStarted(__FUNCTION__);\n\n        $this->updateStatus(false);\n\n        if (null !== $callback) {\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::wait\".');\n            }\n            $this->callback = $this->buildCallback($callback);\n        }\n\n        do {\n            $this->checkTimeout();\n            $running = $this->isRunning() && ('\\\\' === \\DIRECTORY_SEPARATOR || $this->processPipes->areOpen());\n            $this->readPipes($running, '\\\\' !== \\DIRECTORY_SEPARATOR || !$running);\n        } while ($running);\n\n        while ($this->isRunning()) {\n            $this->checkTimeout();\n            usleep(1000);\n        }\n\n        if ($this->processInformation['signaled'] && $this->processInformation['termsig'] !== $this->latestSignal) {\n            throw new ProcessSignaledException($this);\n        }","sourceCodeStart":460,"sourceCodeEnd":496,"githubUrl":"https://github.com/symfony/process/blob/99b85026db14a68f02c6f3eeb01a1170ca25c491/Process.php#L460-L496","documentation":"wait($callback) can only deliver output through a callback if the process pipes have read support — either a callback was given to start() or enableOutput() was called. Otherwise there is nothing to read and calling wait with a callback would hang or lose output, so a LogicException is thrown (after stopping the process) telling you which of the two setup steps is missing.","triggerScenarios":"Calling $process->wait($someCallback) when start() was called without a callback and enableOutput()/enableOutputByDefault() was never invoked; the pipes layer reports haveReadSupport() === false.","commonSituations":"Mixing the run($callback) style with the start()/wait() style; starting a fire-and-forget process then later deciding to consume output; refactors that moved the callback from start() to wait().","solutions":["Pass the callback to start(): $process->start($callback); then call wait().","Call $process->enableOutput() (or Process::enableOutputByDefault()) before start, then wait($callback).","If you don't need output, call wait() without a callback and use getOutput() afterwards.","Unify on one style: either run($callback) or start()/wait($callback) with read support enabled."],"exampleFix":"// before\n$process->start();\n$process->wait(function ($type, $buffer) { echo $buffer; }); // LogicException\n\n// after\n$process->start(function ($type, $buffer) { echo $buffer; });\n$process->wait();","handlingStrategy":"validation","validationCode":"if (!$this->readOutput && null === $callback) {\n    // no read support: either enable output or skip callback use\n}\n$process->enableOutput();\n$process->start();\n$process->wait($callback);","typeGuard":null,"tryCatchPattern":"try {\n    $process->wait($callback);\n} catch (\\LogicException $e) {\n    if (str_contains($e->getMessage(), 'Pass the callback to the \"Process::start\"')) {\n        // restart flow: pass callback to start() or enableOutput() first\n    }\n    throw $e;\n}","preventionTips":["Pick one style: run($callback) or start($callback)/wait(); do not move the callback between calls.","Call enableOutput() (or Process::enableOutputByDefault()) whenever output consumption is planned.","Note wait($callback) also stops the process on this error — restart if you recover.","Add a static-analysis/lint rule or helper wrapper that requires a callback on start when wait will use one."],"tags":["php","symfony-process","callback","output","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"}