{"record":{"id":"a06cefba6bcf5f35","repo":"flarum/framework","slug":"halt-signal-received-killing-to-restart","errorCode":null,"errorMessage":"Halt signal received, killing to restart.","messagePattern":"Halt signal received, killing to restart\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"info","filePath":"extensions/realtime/src/Websocket/Console/ServeCommand.php","lineNumber":180,"sourceCode":"                'data' => ['revision' => $token],\n            ];\n\n            $manager->getChannels()->then(function (array $channels) use ($payload) {\n                foreach ($channels as $name => $channel) {\n                    if ($name === 'public' || str_starts_with($name, 'private-user=')) {\n                        $payload->channel = $name;\n                        $channel->broadcast($payload);\n                    }\n                }\n            });\n        });\n    }\n\n    protected function restartOnCachedSignal(LoopInterface $loop, Repository $cache): void\n    {\n        $loop->addPeriodicTimer(10, function (TimerInterface $timer) use ($loop, $cache) {\n            if ($cache->has($key = HaltCommand::KEY)) {\n                $this->warn('Halt signal received, killing to restart.');\n\n                $cache->forget($key);\n\n                $loop->stop();\n            }\n        });\n    }\n\n    protected function restartOnExtensionChanges(LoopInterface $loop): void\n    {\n        $enabled = null;\n\n        $loop->addPeriodicTimer(10, function (TimerInterface $timer) use ($loop, &$enabled) {\n            $app = $this->getLaravel();\n\n            // If a Redis-backed settings cache is bound (e.g. fof/redis), use the settings\n            // repository which will read from Redis. Otherwise fall back to a direct DB query,\n            // because the default MemoryCacheSettingsRepository is a per-process in-memory cache","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/flarum/framework/blob/4b939f685389bfe8a380e9e28ddf305a1c66950c/extensions/realtime/src/Websocket/Console/ServeCommand.php#L162-L198","documentation":"The realtime websocket server (ServeCommand) polls the cache every 10 seconds for the HaltCommand KEY; when present, it warns 'Halt signal received, killing to restart.', clears the key, and stops the ReactPHP loop so the process exits (typically for a supervisor/systemd to restart it with fresh code/config).","triggerScenarios":"Running `php flarum websocket:serve` (or the serve command) while `php flarum halt` has been executed elsewhere, setting HaltCommand::KEY in the cache.","commonSituations":"Deployments or config changes that call `flarum halt` to signal the long-running websocket process to recycle; admin actions that require a restart.","solutions":["No action needed — this is expected restart behavior","Ensure a process supervisor (systemd, pm2, Docker restart policy) restarts the server after exit","If it loops unexpectedly, inspect who is running `php flarum halt` or what writes HaltCommand::KEY to the cache"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"# ensure a supervisor restarts the websocket server\n# systemd: Restart=always under [Service]","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always run websocket:serve under a supervisor with auto-restart","Coordinate `flarum halt` calls with deployment tooling","Monitor the process and the halt cache key when diagnosing restart loops"],"tags":["websocket","restart-signal","reactphp"],"backgroundTag":"internal-invariant-violation","analyzedSha":"4b939f685389bfe8a380e9e28ddf305a1c66950c","analyzedAt":"2026-09-15T18:09:20.879Z","contentChangedAt":"2026-09-15T18:09:20.879Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}