{"record":{"id":"6070e39b26afc0a1","repo":"chenhg5/cc-connect","slug":"work-dir-is-not-accessible-s-w","errorCode":null,"errorMessage":"work_dir is not accessible: %s: %w","messagePattern":"work_dir is not accessible: (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/setup.go","lineNumber":523,"sourceCode":"\t}\n\tmgmtJSON(w, http.StatusCreated, map[string]any{\n\t\t\"message\":          fmt.Sprintf(\"platform %q added to project %q\", req.Type, projectName),\n\t\t\"restart_required\": true,\n\t})\n}\n\nfunc validateProjectWorkDir(workDir string) (string, error) {\n\ttrimmed := strings.TrimSpace(workDir)\n\tif trimmed == \"\" {\n\t\treturn \"\", nil\n\t}\n\n\tinfo, err := os.Stat(trimmed)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn \"\", fmt.Errorf(\"work_dir does not exist: %s\", trimmed)\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"work_dir is not accessible: %s: %w\", trimmed, err)\n\t}\n\tif !info.IsDir() {\n\t\treturn \"\", fmt.Errorf(\"work_dir is not a directory: %s\", trimmed)\n\t}\n\treturn trimmed, nil\n}\n","sourceCodeStart":505,"sourceCodeEnd":530,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/setup.go#L505-L530","documentation":"validateProjectWorkDir throws this when os.Stat on the work_dir fails with an error other than NotExist (e.g. permission denied on a parent directory, I/O error). The underlying OS error is wrapped with %w so errors.Is/As still work on the cause.","triggerScenarios":"Statting a path whose parent directory denies traversal permission, a path on an unmounted filesystem, a broken automount, or a dangling symlink where the link's target dir is inaccessible, during project save/detail handlers.","commonSituations":"work_dir under /root or another user's home while cc-connect runs as a different user; NFS/SMB mount down; sandboxed service (systemd) lacking access to the user's home directory.","solutions":["Run `ls -ld` on the path and each parent to find where permission is denied; chown/chmod or move the work_dir somewhere the service user can access","Inspect the wrapped cause with errors.Is(err, fs.ErrPermission) etc. to identify the exact OS error","If running under systemd, add ReadWritePaths=/supplementary groups to the unit for the directory"],"exampleFix":"// before\nwork_dir = \"/root/projects/app\"   # service runs as cconnect\n// after\nwork_dir = \"/srv/projects/app\"    # chown cconnect:cconnect /srv/projects/app","handlingStrategy":"validation","validationCode":"wd := strings.TrimSpace(cfg.WorkDir)\nfor p := wd; p != filepath.Dir(p); p = filepath.Dir(p) {\n    if _, err := os.Stat(p); err != nil {\n        return fmt.Errorf(\"cannot access %s: %w\", p, err)\n    }\n}\nif err := unix.Access(wd, unix.W_OK); err != nil {\n    return fmt.Errorf(\"no write access to %s: %w\", wd, err)\n}","typeGuard":"func canAccessDir(path string) bool {\n    f, err := os.Open(path)\n    if err != nil { return false }\n    defer f.Close()\n    if _, err := f.Readdirnames(1); err != nil && err != io.EOF { return false }\n    return true\n}","tryCatchPattern":"dir, err := validateProjectWorkDir(cfg.WorkDir)\nif err != nil {\n    var pe *fs.PathError\n    if errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrPermission) {\n        slog.Error(\"work_dir permission denied; run as a user with access or fix ownership\", \"path\", pe.Path)\n    }\n    return err\n}","preventionTips":["Run cc-connect as a user that owns (or can traverse) every project work_dir","Avoid work_dirs under /root or other users' homes when running as a service","Check systemd unit ReadWritePaths/ProtectHome settings that may sandbox directory access"],"tags":["filesystem","permissions","config"],"backgroundTag":"permission-denied","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"}