chenhg5/cc-connect · warning
chmod socket: %w
Error message
chmod socket: %w
What it means
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.
Source
Thrown at core/api.go:82
// NewAPIServer creates an API server on a Unix socket.
func NewAPIServer(dataDir string) (*APIServer, error) {
sockDir := filepath.Join(dataDir, "run")
if err := os.MkdirAll(sockDir, 0o755); err != nil {
return nil, fmt.Errorf("create run dir: %w", err)
}
sockPath := filepath.Join(sockDir, "api.sock")
// Remove stale socket
os.Remove(sockPath)
listener, err := net.Listen("unix", sockPath)
if err != nil {
return nil, fmt.Errorf("listen unix socket: %w", err)
}
if err := os.Chmod(sockPath, 0o600); err != nil {
_ = listener.Close()
return nil, fmt.Errorf("chmod socket: %w", err)
}
s := &APIServer{
socketPath: sockPath,
listener: listener,
mux: http.NewServeMux(),
engines: make(map[string]*Engine),
maxAttachmentBytes: DefaultMaxAttachmentSize,
}
s.mux.HandleFunc("/send", s.handleSend)
s.mux.HandleFunc("/sessions", s.handleSessions)
s.mux.HandleFunc("/cron/add", s.handleCronAdd)
s.mux.HandleFunc("/cron/list", s.handleCronList)
s.mux.HandleFunc("/cron/info", s.handleCronInfo)
s.mux.HandleFunc("/cron/edit", s.handleCronEdit)
s.mux.HandleFunc("/cron/del", s.handleCronDel)
s.mux.HandleFunc("/timer/add", s.handleTimerAdd)
s.mux.HandleFunc("/timer/list", s.handleTimerList)View on GitHub (pinned to 4000b2338a)
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
Example fix
// before
if err := os.Chmod(sockPath, 0o600); err != nil {
_ = listener.Close()
return nil, fmt.Errorf("chmod socket: %w", err)
}
// after
if err := os.Chmod(sockPath, 0o600); err != nil {
slog.Warn("cannot chmod api socket; relying on directory permissions", "err", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
runDir := filepath.Join(dataDir, "run")
if fi, err := os.Stat(runDir); err == nil {
// Unix perms supported only on unix-capable local FS; NFS/Windows mounts will fail chmod
_ = fi
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- create run dir: %w
- read existing Agy hooks %s: %w
- kimi: read sessions dir: %w
- create systemd dir: %w
- write unit file: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/5a0ac72cf48de3ed.
Report an issue: GitHub.