{"record":{"id":"f8914637d3f4ed9c","repo":"chenhg5/cc-connect","slug":"listen-unix-socket-w","errorCode":null,"errorMessage":"listen unix socket: %w","messagePattern":"listen unix socket: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/api.go","lineNumber":78,"sourceCode":"\tVideos     []FileAttachment  `json:\"videos,omitempty\"`\n\tAtUsers    []string          `json:\"at_users,omitempty\"`\n\tAtAll      bool              `json:\"at_all,omitempty\"`\n}\n\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)","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/api.go#L60-L96","documentation":"NewAPIServer binds a Unix domain socket at <dataDir>/run/api.sock via net.Listen(\"unix\", ...); failure is wrapped as \"listen unix socket: %w\". Common causes: another instance is already bound (address in use), the path is too long, or a stale socket file is unremovable/locked.","triggerScenarios":"Calling NewAPIServer while a previous cc-connect instance is still running and holding api.sock; the stale-socket os.Remove fails due to permissions; the resolved socket path exceeds the ~104/108 byte sun_path limit; the run directory was made non-writable between mkdir and listen.","commonSituations":"Starting a second daemon against the same dataDir; a crashed instance left a socket that a service user cannot delete; deep dataDir path making the socket path too long; container restart where old socket persists in a mounted volume.","solutions":["Stop the existing instance holding api.sock (check with `lsof <dataDir>/run/api.sock` or `ss -x | grep api.sock`), then retry","Remove the stale socket manually if the process failed to: rm <dataDir>/run/api.sock","Shorten dataDir so the full socket path stays under ~100 characters","Ensure the run directory is writable by the process user","Check the wrapped inner error: 'address already in use' vs 'permission denied' point to different fixes"],"exampleFix":"// before\nsrv, err := core.NewAPIServer(\"/var/lib/cc-connect\") // second instance\n// after\nif _, err := os.Stat(\"/var/lib/cc-connect/run/api.sock\"); err == nil {\n    return errors.New(\"cc-connect already running (api.sock exists)\")\n}\nsrv, err := core.NewAPIServer(\"/var/lib/cc-connect\")","handlingStrategy":"validation","validationCode":"sock := filepath.Join(dataDir, \"run\", \"api.sock\")\nif len(sock) > 100 {\n    return fmt.Errorf(\"socket path too long (%d bytes): %s\", len(sock), sock)\n}\nif _, err := os.Stat(sock); err == nil {\n    return fmt.Errorf(\"socket already exists; is cc-connect already running?\")\n}","typeGuard":null,"tryCatchPattern":"srv, err := core.NewAPIServer(dataDir)\nif err != nil {\n    if strings.Contains(err.Error(), \"listen unix socket\") && strings.Contains(err.Error(), \"address already in use\") {\n        slog.Error(\"another cc-connect instance is running; stop it or use a different dataDir\")\n    }\n    return err\n}","preventionTips":["Use a PID/lockfile to prevent two instances on the same dataDir","Clean up stale sockets on graceful shutdown","Keep dataDir shallow so the socket path fits sun_path limits","For systemd, use Restart=on-failure with a small delay to avoid socket races"],"tags":["unix-socket","network","address-in-use","go"],"backgroundTag":"address-already-in-use","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}