docs/gateway/gateway-lock.md
Startup enforces single-instance ownership in two independent steps, in order:
ws://127.0.0.1:18789) as an exclusive TCP listener.Each layer can fail independently and throws its own GatewayLockError.
If the lock file is missing, the recorded owner process is gone, or the owner's port probe shows no live listener, startup reclaims the lock and continues.
If the lock is actively held and none of the above apply, startup retries for up to 5 seconds (default) before giving up:
GatewayLockError("gateway already running (pid <pid>); lock timeout after <ms>ms")
On EADDRINUSE, startup retries the bind for up to 20 attempts at 500ms intervals (roughly 10 seconds total) to ride out a TIME_WAIT window after a recently exited process.
If the port is still in use after retries:
GatewayLockError("another gateway instance is already listening on ws://127.0.0.1:<port>")
Other bind failures:
GatewayLockError("failed to bind gateway socket on ws://127.0.0.1:<port>: <cause>")
On shutdown, the gateway closes the HTTP/WebSocket server and removes the lock file.
openclaw gateway --port <port>./healthz on the existing process. If that process is healthy, the new process leaves it in control instead of failing. On systemd, it exits with code 78; the unit's RestartPreventExitStatus=78 stops Restart=always from looping on a lock or EADDRINUSE conflict. If the existing process never becomes healthy, the health-probe retry is time-bounded and startup then fails with the lock error above instead of looping forever.EADDRINUSE and port conflicts