{"record":{"id":"009d1f8db49a1ed0","repo":"joewalnes/websocketd","slug":"socket-s-is-already-in-use-by-a-running-server","errorCode":null,"errorMessage":"socket %s is already in use by a running server","messagePattern":"socket (.+?) is already in use by a running server","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"main.go","lineNumber":146,"sourceCode":"// timeout only guards against a pathological listener that accepts nothing.\nconst unixSocketProbeTimeout = time.Second\n\n// serveUnixSocket removes a stale socket file left behind by an unclean\n// shutdown (if any) and then serves on it. It only ever removes a path that\n// is actually a socket, never an arbitrary file that happens to be there.\n//\n// A socket file at the path may equally belong to a server that is still\n// running, so removing it unconditionally is not safe: unlinking a live\n// server's socket and binding a new one in its place leaves that process\n// running but permanently unreachable, with no error on either side. Probing\n// first tells the two apart — a successful dial means someone is listening, a\n// refused connection means the file is stale. Refusing to start on a live\n// socket matches what a TCP listener already does when its port is taken.\nfunc serveUnixSocket(path string, config *Config, log *libwebsocketd.LogScope) error {\n\tif info, err := os.Stat(path); err == nil && info.Mode()&os.ModeSocket != 0 {\n\t\tif conn, err := net.DialTimeout(\"unix\", path, unixSocketProbeTimeout); err == nil {\n\t\t\tconn.Close()\n\t\t\treturn fmt.Errorf(\"socket %s is already in use by a running server\", path)\n\t\t}\n\t\tif err := os.Remove(path); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to remove stale socket %s: %w\", path, err)\n\t\t}\n\t}\n\treturn serve(\"unix\", path, config, log)\n}\n\n// redirectAddress returns addr with its port replaced by redirPort. IPv6\n// literals must be split with net.SplitHostPort (which understands brackets);\n// splitting on the first colon lands inside \"[::1]:port\" and produced a\n// malformed listener address that failed to bind — and, being a listener\n// error, killed every other listener too.\nfunc redirectAddress(addr string, redirPort int) (string, error) {\n\thost, _, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"cannot derive redirect address from %q: %w\", addr, err)\n\t}","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/joewalnes/websocketd/blob/7a8683dc7f9778dc615945aaed2a8dc77290227b/main.go#L128-L164","documentation":"Before serving on a Unix socket path, websocketd checks an existing socket file by dialing it. If the connect succeeds within the 1s probe timeout, a live server is still listening; starting a second instance would orphan the first, so startup is refused with this error instead of silently stealing the socket.","triggerScenarios":"Running `websocketd --unixsocket=/run/app.sock ...` (via intArg/main) while another websocketd (or any AF_UNIX listener) already owns that socket path and accepts connections.","commonSituations":"Duplicate systemd unit instance; forgetting a previous dev instance in another terminal; supervisor restarting the app before the old process exits; running the same command in two containers sharing a host-mounted socket directory.","solutions":["Stop the existing process: find it with `lsof /run/app.sock` or `ss -x | grep app.sock`, then kill/restart it properly.","If the old instance should have died, ensure your supervisor waits for process exit before restarting.","Use a distinct socket path per instance (e.g. include a PID or environment name).","Note this is deliberate protection: a stale socket is removed automatically; only a live listener triggers this error, so don't force-delete the file — that would strand the running server."],"exampleFix":"# before (fails while old server still runs)\nwebsocketd --unixsocket=/run/app.sock ./handler\n# after\nsystemctl stop websocketd-old && websocketd --unixsocket=/run/app.sock ./handler","handlingStrategy":"try-catch","validationCode":"#!/bin/sh\nif lsof \"${SOCKET_PATH}\" >/dev/null 2>&1; then echo \"socket in use; stopping old instance\"; pkill -f \"unixsocket=${SOCKET_PATH}\" || exit 1; fi","typeGuard":null,"tryCatchPattern":"out, err := exec.Command(\"websocketd\", \"--unixsocket\", sock, ...).CombinedOutput()\nif err != nil && strings.Contains(string(out), \"already in use by a running server\") {\n    stopExistingInstance(sock) // systemd stop / pkill, then retry once\n    err = exec.Command(\"websocketd\", \"--unixsocket\", sock, ...).Run()\n}","preventionTips":["Run websocketd under a supervisor that guarantees single-instance semantics.","Include the environment/instance name in the socket path to avoid collisions.","Wait for full process exit before restarts (systemd's default stop sequence).","Use `ss -x` or `lsof` in deploy scripts to detect a live owner before starting."],"tags":["unix-socket","address-in-use","startup","conflict"],"backgroundTag":"address-already-in-use","analyzedSha":"7a8683dc7f9778dc615945aaed2a8dc77290227b","analyzedAt":"2026-09-03T13:52:22.309Z","contentChangedAt":"2026-09-03T13:52:22.309Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}