joewalnes/websocketd · error

failed to chmod unix socket %s to %o: %w

Error message

failed to chmod unix socket %s to %o: %w

What it means

After binding a Unix-domain socket, websocketd applies config.SocketMode via os.Chmod when --socket-mode (a non-zero value) is set, closing the window in which the umask default exposes the socket. If the chmod syscall fails, the listener is closed and startup aborts with this wrapped error (original errno included via %w).

Source

Thrown at main.go:83

	fmt.Printf("%s | %-6s | %-10s | %s | %s\n", libwebsocketd.Timestamp(), levelName, category, assocDump, escapeControls(fullMsg))
	l.Mutex.Unlock()
}

// serve listens on the given network ("tcp" or "unix") and address/path and
// runs an HTTP(S) server on it, honoring the Ssl/mutual-TLS config. It blocks
// until the listener errors out.
func serve(network, address string, config *Config, log *libwebsocketd.LogScope) error {
	listener, err := net.Listen(network, address)
	if err != nil {
		return err
	}
	// Pin the Unix socket's permissions when asked: the umask default can
	// leave the socket connectable by other local users. Chmod immediately
	// after bind so the umask-derived window is as short as it can be.
	if network == "unix" && config.SocketMode != 0 {
		if err := os.Chmod(address, config.SocketMode); err != nil {
			listener.Close()
			return fmt.Errorf("failed to chmod unix socket %s to %o: %w", address, config.SocketMode, err)
		}
	}
	if !config.Ssl {
		return (&http.Server{ReadHeaderTimeout: readHeaderTimeout}).Serve(listener)
	}
	if config.SslCaFile != "" {
		return serveMutualTLS(listener, config.CertFile, config.KeyFile, config.SslCaFile, log)
	}
	server := &http.Server{ReadHeaderTimeout: readHeaderTimeout, TLSConfig: tlsConfig()}
	return server.ServeTLS(listener, config.CertFile, config.KeyFile)
}

// tlsConfig returns the base TLS settings shared by all HTTPS servers. It pins
// a minimum protocol version explicitly rather than relying on the Go default,
// which has drifted across releases.
func tlsConfig() *tls.Config {
	return &tls.Config{MinVersion: tls.VersionTLS12}
}

View on GitHub (pinned to 7a8683dc7f)

Solutions

  1. Check the wrapped errno in the message and verify the process owns the socket file (bind should have created it as the running user).
  2. Move the socket path to a directory writable by the service user (e.g. /run/websocketd/) instead of a system-managed location.
  3. Remove the sandbox/mount restriction blocking chmod, or disable --socket-mode (0 = umask default) if permissions are handled elsewhere.
  4. Confirm no competing process (cleanup unit, another instance) is deleting the socket during startup.

Example fix

# before
websocketd --unixsocket=/var/run/app.sock --socket-mode=660 ./handler
# after (socket in a directory the service user controls)
websocketd --unixsocket=/run/websocketd/app.sock --socket-mode=660 ./handler
Defensive patterns

Strategy: validation

Validate before calling

const fi, _ := os.Stat(socketPath)
if err := syscall.Access(filepath.Dir(socketPath), syscall.W_OK); err != nil {
    log.Fatalf("cannot chmod socket %s: no write access to %s: %v", socketPath, filepath.Dir(socketPath), err)
}
_ = fi

Try / catch

if err := runWebsocketd(); err != nil {
    var pathErr *os.PathError
    if errors.As(err, &pathErr) && strings.Contains(err.Error(), "failed to chmod unix socket") {
        log.Fatalf("socket chmod failed at %s: %v — check ownership of the socket directory", pathErr.Path, pathErr.Err)
    }
    return err
}

Prevention

When it happens

Trigger: os.Chmod on the freshly bound socket path fails: the process lost write/ownership rights on the path's directory between bind and chmod, the filesystem disallows chmod (some network mounts), the socket was unlinked by another process mid-startup, or an invalid mode was requested.

Common situations: Running under systemd with restrictive sandboxing (ProtectSystem, PrivateTmp) on a socket in a protected directory; shared tmpfs/NFS where chmod isn't supported; another service's cleanup racing to delete the socket file.

Related errors


AI-assisted analysis of joewalnes/websocketd@7a8683dc7f (2026-09-03). Data as JSON: /api/errors/b1103eca8374fca5. Report an issue: GitHub.