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

  1. Move dataDir/run onto a local filesystem that supports Unix permissions (ext4, apfs, tmpfs)
  2. 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
  3. Check the wrapped inner error for EPERM/EOPNOTSUPP to confirm filesystem support
  4. 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

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


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/5a0ac72cf48de3ed. Report an issue: GitHub.