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

  1. chown the work_dir to the target user: sudo chown -R <run_as_user> <work_dir>
  2. 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>
  3. Ensure every parent directory grants the target user traverse (x) permission
  4. Re-run the check: sudo -n -iu <user> -- test -r <dir> -a -w <dir> && echo ok
  5. 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

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


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