chenhg5/cc-connect · critical
project %q: target user %q cannot read AND write work_dir %q
Error message
project %q: target user %q cannot read AND write work_dir %q. Agents will fail with EACCES at runtime. Fix ownership/permissions on this directory (chown/chmod or an ACL granting the target user rwx) before starting cc-connect.
What it means
PreflightRunAsUser (core/runas_check.go:128) checks that the run-as target user can both read and write cfg.WorkDir by running 'sudo -n -iu <user> -- test -r <dir> -a -w <dir>'. Failure means ownership/permissions are wrong and the agent would hit EACCES at runtime, so it is recorded as a fatal startup error with remediation guidance.
Source
Thrown at core/runas_check.go:128
cfg.Project, cfg.RunAsUser)
if result.SudoListOutput != "" {
msg += "\n\n`sudo -n -l` as " + cfg.RunAsUser + ":\n" + indent(result.SudoListOutput, " ")
}
result.Fatal = append(result.Fatal, errors.New(msg))
// Don't return — still run check 3 so the operator gets all
// the bad news in a single startup attempt.
}
if cfg.WorkDir == "" {
result.Warnings = append(result.Warnings, fmt.Sprintf(
"project %q: no work_dir configured; skipping filesystem access checks", cfg.Project))
} else {
absWorkDir := cfg.WorkDir
if abs, err := filepath.Abs(absWorkDir); err == nil {
absWorkDir = abs
}
if _, err := cfg.Runner.Run(ctx, "-n", "-iu", cfg.RunAsUser, "--", "test", "-r", absWorkDir, "-a", "-w", absWorkDir); err != nil {
result.Fatal = append(result.Fatal, fmt.Errorf(
"project %q: target user %q cannot read AND write work_dir %q. Agents will fail with EACCES at runtime. Fix ownership/permissions on this directory (chown/chmod or an ACL granting the target user rwx) before starting cc-connect.",
cfg.Project, cfg.RunAsUser, absWorkDir))
} else {
warn := scanDescendants(ctx, cfg.Runner, cfg.RunAsUser, absWorkDir, cfg.ScanConfig)
if warn != "" {
result.Warnings = append(result.Warnings, warn)
}
}
}
return result
}
// scanDescendants runs find as the target user under workDir and
// returns a formatted warning string, or "" if nothing is flagged.
// Respects ScanConfig.Timeout. Output format per line is
// "MODE<TAB>PATH" where MODE is noread / nowrite / nosearch.
func scanDescendants(ctx context.Context, runner SudoRunner, target, workDir string, scan DescendantScanConfig) string {View on GitHub (pinned to 4000b2338a)
Solutions
- chown the work_dir to the target user: sudo chown -R <run_as_user> <work_dir>
- Or grant rwx via chmod (e.g. chmod 770 with a shared group) or a POSIX ACL: setfacl -R -m u:<run_as_user>:rwx <work_dir>
- Ensure every parent directory grants the target user traverse (x) permission
- Re-run the check: sudo -n -iu <user> -- test -r <dir> -a -w <dir> && echo ok
- Re-run cc-connect doctor to confirm the fatal is gone
Example fix
// before sudo mkdir -p /srv/work && sudo tar -xzf proj.tgz -C /srv/work # root-owned, 0755 // agent fails preflight: cannot read AND write work_dir // after sudo chown -R agent1:agent1 /srv/work
Defensive patterns
Strategy: validation
Validate before calling
func workDirAccessible(target, dir string) error {
abs, err := filepath.Abs(dir); if err != nil { return err }
cmd := exec.Command("sudo", "-n", "-iu", target, "--", "test", "-r", abs, "-a", "-w", abs)
if err := cmd.Run(); err != nil { return fmt.Errorf("%s cannot r/w %s", target, abs) }
return nil
}
// call before starting cc-connect with run_as_user Try / catch
result := core.PreflightRunAsUser(ctx, cfg)
for _, f := range result.Fatal {
if strings.Contains(f.Error(), "cannot read AND write work_dir") {
fmt.Fprintf(os.Stderr, "FATAL (fix ownership/permissions, e.g. chown -R %s %s): %v\n", cfg.RunAsUser, cfg.WorkDir, f)
os.Exit(1)
}
} Prevention
- Provision work_dir with the target user as owner at deploy time (chown -R <run_as_user>)
- Remember parent directories need the x (traverse) bit for the target user
- For shared dirs use group ownership or ACLs (setfacl -m u:<target>:rwx) instead of world-writable modes
- Re-run preflight after moving projects or changing mounts/volumes
When it happens
Trigger: The work_dir is owned by root or another user, has mode without rwx for the target (e.g. 0750 root:root), lacks the execute bit needed to traverse, or sits under a parent directory the target user cannot traverse. Raised during doctor/preflight when the test -r -a -w probe exits non-zero.
Common situations: Directory created by root during provisioning with restrictive umask; work_dir owned by the supervisor instead of the agent user; project path moved and ownership not migrated; container volume mounted root-owned.
Understand the failure class
Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.
Related errors
- read existing Agy hooks %s: %w
- claudecode: create usage temp dir: %w
- claudecode: chmod per-spawn prompt file 0o644: %w
- codex app-server: create image dir: %w
- codex: mkdir codex home: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/b8f35df1da954f05.
Report an issue: GitHub.