joewalnes/websocketd · error
socket %s is already in use by a running server
Error message
socket %s is already in use by a running server
What it means
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.
Source
Thrown at main.go:146
// timeout only guards against a pathological listener that accepts nothing.
const unixSocketProbeTimeout = time.Second
// serveUnixSocket removes a stale socket file left behind by an unclean
// shutdown (if any) and then serves on it. It only ever removes a path that
// is actually a socket, never an arbitrary file that happens to be there.
//
// A socket file at the path may equally belong to a server that is still
// running, so removing it unconditionally is not safe: unlinking a live
// server's socket and binding a new one in its place leaves that process
// running but permanently unreachable, with no error on either side. Probing
// first tells the two apart — a successful dial means someone is listening, a
// refused connection means the file is stale. Refusing to start on a live
// socket matches what a TCP listener already does when its port is taken.
func serveUnixSocket(path string, config *Config, log *libwebsocketd.LogScope) error {
if info, err := os.Stat(path); err == nil && info.Mode()&os.ModeSocket != 0 {
if conn, err := net.DialTimeout("unix", path, unixSocketProbeTimeout); err == nil {
conn.Close()
return fmt.Errorf("socket %s is already in use by a running server", path)
}
if err := os.Remove(path); err != nil {
return fmt.Errorf("failed to remove stale socket %s: %w", path, err)
}
}
return serve("unix", path, config, log)
}
// redirectAddress returns addr with its port replaced by redirPort. IPv6
// literals must be split with net.SplitHostPort (which understands brackets);
// splitting on the first colon lands inside "[::1]:port" and produced a
// malformed listener address that failed to bind — and, being a listener
// error, killed every other listener too.
func redirectAddress(addr string, redirPort int) (string, error) {
host, _, err := net.SplitHostPort(addr)
if err != nil {
return "", fmt.Errorf("cannot derive redirect address from %q: %w", addr, err)
}View on GitHub (pinned to 7a8683dc7f)
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.
Example fix
# before (fails while old server still runs) websocketd --unixsocket=/run/app.sock ./handler # after systemctl stop websocketd-old && websocketd --unixsocket=/run/app.sock ./handler
Defensive patterns
Strategy: try-catch
Validate before calling
#!/bin/sh
if lsof "${SOCKET_PATH}" >/dev/null 2>&1; then echo "socket in use; stopping old instance"; pkill -f "unixsocket=${SOCKET_PATH}" || exit 1; fi Try / catch
out, err := exec.Command("websocketd", "--unixsocket", sock, ...).CombinedOutput()
if err != nil && strings.Contains(string(out), "already in use by a running server") {
stopExistingInstance(sock) // systemd stop / pkill, then retry once
err = exec.Command("websocketd", "--unixsocket", sock, ...).Run()
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed to chmod unix socket %s to %o: %w
- failed to remove stale socket %s: %w
- cannot derive redirect address from %q: %w
AI-assisted analysis of joewalnes/websocketd@7a8683dc7f (2026-09-03).
Data as JSON: /api/errors/009d1f8db49a1ed0.
Report an issue: GitHub.