{"record":{"id":"b1103eca8374fca5","repo":"joewalnes/websocketd","slug":"failed-to-chmod-unix-socket-s-to-o-w","errorCode":null,"errorMessage":"failed to chmod unix socket %s to %o: %w","messagePattern":"failed to chmod unix socket (.+?) to %o: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"main.go","lineNumber":83,"sourceCode":"\tfmt.Printf(\"%s | %-6s | %-10s | %s | %s\\n\", libwebsocketd.Timestamp(), levelName, category, assocDump, escapeControls(fullMsg))\n\tl.Mutex.Unlock()\n}\n\n// serve listens on the given network (\"tcp\" or \"unix\") and address/path and\n// runs an HTTP(S) server on it, honoring the Ssl/mutual-TLS config. It blocks\n// until the listener errors out.\nfunc serve(network, address string, config *Config, log *libwebsocketd.LogScope) error {\n\tlistener, err := net.Listen(network, address)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// Pin the Unix socket's permissions when asked: the umask default can\n\t// leave the socket connectable by other local users. Chmod immediately\n\t// after bind so the umask-derived window is as short as it can be.\n\tif network == \"unix\" && config.SocketMode != 0 {\n\t\tif err := os.Chmod(address, config.SocketMode); err != nil {\n\t\t\tlistener.Close()\n\t\t\treturn fmt.Errorf(\"failed to chmod unix socket %s to %o: %w\", address, config.SocketMode, err)\n\t\t}\n\t}\n\tif !config.Ssl {\n\t\treturn (&http.Server{ReadHeaderTimeout: readHeaderTimeout}).Serve(listener)\n\t}\n\tif config.SslCaFile != \"\" {\n\t\treturn serveMutualTLS(listener, config.CertFile, config.KeyFile, config.SslCaFile, log)\n\t}\n\tserver := &http.Server{ReadHeaderTimeout: readHeaderTimeout, TLSConfig: tlsConfig()}\n\treturn server.ServeTLS(listener, config.CertFile, config.KeyFile)\n}\n\n// tlsConfig returns the base TLS settings shared by all HTTPS servers. It pins\n// a minimum protocol version explicitly rather than relying on the Go default,\n// which has drifted across releases.\nfunc tlsConfig() *tls.Config {\n\treturn &tls.Config{MinVersion: tls.VersionTLS12}\n}","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/joewalnes/websocketd/blob/7a8683dc7f9778dc615945aaed2a8dc77290227b/main.go#L65-L101","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the wrapped errno in the message and verify the process owns the socket file (bind should have created it as the running user).","Move the socket path to a directory writable by the service user (e.g. /run/websocketd/) instead of a system-managed location.","Remove the sandbox/mount restriction blocking chmod, or disable --socket-mode (0 = umask default) if permissions are handled elsewhere.","Confirm no competing process (cleanup unit, another instance) is deleting the socket during startup."],"exampleFix":"# before\nwebsocketd --unixsocket=/var/run/app.sock --socket-mode=660 ./handler\n# after (socket in a directory the service user controls)\nwebsocketd --unixsocket=/run/websocketd/app.sock --socket-mode=660 ./handler","handlingStrategy":"validation","validationCode":"const fi, _ := os.Stat(socketPath)\nif err := syscall.Access(filepath.Dir(socketPath), syscall.W_OK); err != nil {\n    log.Fatalf(\"cannot chmod socket %s: no write access to %s: %v\", socketPath, filepath.Dir(socketPath), err)\n}\n_ = fi","typeGuard":null,"tryCatchPattern":"if err := runWebsocketd(); err != nil {\n    var pathErr *os.PathError\n    if errors.As(err, &pathErr) && strings.Contains(err.Error(), \"failed to chmod unix socket\") {\n        log.Fatalf(\"socket chmod failed at %s: %v — check ownership of the socket directory\", pathErr.Path, pathErr.Err)\n    }\n    return err\n}","preventionTips":["Place sockets in a directory owned by the service user (systemd RuntimeDirectory).","Review sandbox settings (ProtectSystem, ReadOnlyPaths) that block chmod on the socket path.","Avoid network filesystems for Unix sockets; use local tmpfs/disk.","Smoke-test the exact socket path and mode as the same user the daemon runs as."],"tags":["unix-socket","permissions","chmod","startup"],"backgroundTag":"chmod-failed","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"}