{"record":{"id":"5a0ac72cf48de3ed","repo":"chenhg5/cc-connect","slug":"chmod-socket-w","errorCode":null,"errorMessage":"chmod socket: %w","messagePattern":"chmod socket: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/api.go","lineNumber":82,"sourceCode":"\n// NewAPIServer creates an API server on a Unix socket.\nfunc NewAPIServer(dataDir string) (*APIServer, error) {\n\tsockDir := filepath.Join(dataDir, \"run\")\n\tif err := os.MkdirAll(sockDir, 0o755); err != nil {\n\t\treturn nil, fmt.Errorf(\"create run dir: %w\", err)\n\t}\n\tsockPath := filepath.Join(sockDir, \"api.sock\")\n\n\t// Remove stale socket\n\tos.Remove(sockPath)\n\n\tlistener, err := net.Listen(\"unix\", sockPath)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"listen unix socket: %w\", err)\n\t}\n\tif err := os.Chmod(sockPath, 0o600); err != nil {\n\t\t_ = listener.Close()\n\t\treturn nil, fmt.Errorf(\"chmod socket: %w\", err)\n\t}\n\n\ts := &APIServer{\n\t\tsocketPath:         sockPath,\n\t\tlistener:           listener,\n\t\tmux:                http.NewServeMux(),\n\t\tengines:            make(map[string]*Engine),\n\t\tmaxAttachmentBytes: DefaultMaxAttachmentSize,\n\t}\n\ts.mux.HandleFunc(\"/send\", s.handleSend)\n\ts.mux.HandleFunc(\"/sessions\", s.handleSessions)\n\ts.mux.HandleFunc(\"/cron/add\", s.handleCronAdd)\n\ts.mux.HandleFunc(\"/cron/list\", s.handleCronList)\n\ts.mux.HandleFunc(\"/cron/info\", s.handleCronInfo)\n\ts.mux.HandleFunc(\"/cron/edit\", s.handleCronEdit)\n\ts.mux.HandleFunc(\"/cron/del\", s.handleCronDel)\n\ts.mux.HandleFunc(\"/timer/add\", s.handleTimerAdd)\n\ts.mux.HandleFunc(\"/timer/list\", s.handleTimerList)","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/api.go#L64-L100","documentation":"After binding the socket, NewAPIServer restricts it with os.Chmod(sockPath, 0o600) so only the owner can connect; failure is wrapped as \"chmod socket: %w\". The listener is closed before returning, so this error aborts server startup.","triggerScenarios":"os.Chmod fails on the freshly created socket file, typically because the filesystem does not support Unix permission bits (e.g. some NFS mounts, Windows filesystems) or because ownership changed between listen and chmod.","commonSituations":"dataDir on an NFS/SMB mount without chmod support; running on a platform (Windows) where Unix socket permissions are unsupported; security software or concurrent process altering the socket file mid-setup.","solutions":["Move dataDir/run onto a local filesystem that supports Unix permissions (ext4, apfs, tmpfs)","If permissions semantics are guaranteed elsewhere (e.g. socket pre-umask), treat chmod failure as non-fatal: log a warning instead of failing — but note current code returns the error","Check the wrapped inner error for EPERM/EOPNOTSUPP to confirm filesystem support","Ensure no other process is manipulating files in the run directory during startup"],"exampleFix":"// before\nif err := os.Chmod(sockPath, 0o600); err != nil {\n    _ = listener.Close()\n    return nil, fmt.Errorf(\"chmod socket: %w\", err)\n}\n// after\nif err := os.Chmod(sockPath, 0o600); err != nil {\n    slog.Warn(\"cannot chmod api socket; relying on directory permissions\", \"err\", err)\n}","handlingStrategy":"try-catch","validationCode":"runDir := filepath.Join(dataDir, \"run\")\nif fi, err := os.Stat(runDir); err == nil {\n    // Unix perms supported only on unix-capable local FS; NFS/Windows mounts will fail chmod\n    _ = fi\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Place the run directory on a local FS supporting Unix permissions (tmpfs is a good default)","Set the run directory mode to 0700 so socket perms matter less","Avoid NFS/SMB mounts for runtime socket data","Test startup on every target platform/container base image"],"tags":["unix-socket","filesystem","permissions","go"],"backgroundTag":"permission-denied","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}