{"record":{"id":"121c1d27cf734bb2","repo":"chenhg5/cc-connect","slug":"verifyrunasusercheap-runasuser-is-empty","errorCode":null,"errorMessage":"VerifyRunAsUserCheap: runAsUser is empty","messagePattern":"VerifyRunAsUserCheap: runAsUser is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/runas.go","lineNumber":237,"sourceCode":"// VerifyRunAsUserCheap runs the two cheap preflight checks that must pass\n// before every spawn, not just at startup:\n//\n//  1. `sudo -n -iu <user> -- /usr/bin/true` must succeed — the supervisor still\n//     has passwordless sudo to the target user.\n//  2. `sudo -n -iu <user> -- sudo -n /usr/bin/true` must FAIL — the target user\n//     cannot non-interactively escalate.\n//\n// Returns nil if both checks behave as expected. Results are cached for\n// verifyCacheTTL keyed by runAsUser so rapid-fire messages don't pay the\n// ~100ms cost per spawn. A failure evicts the cache immediately so the\n// next spawn re-verifies fresh.\n//\n// The expensive checks (work_dir access, isolation probe) live in the\n// preflight and audit packages and only run at startup / via `cc-connect\n// doctor user-isolation`.\nfunc VerifyRunAsUserCheap(ctx context.Context, runner SudoRunner, runAsUser string) error {\n\tif runAsUser == \"\" {\n\t\treturn errors.New(\"VerifyRunAsUserCheap: runAsUser is empty\")\n\t}\n\tif verifyCacheHit(runAsUser) {\n\t\treturn nil\n\t}\n\tif out, err := runner.Run(ctx, \"-n\", \"-iu\", runAsUser, \"--\", \"/usr/bin/true\"); err != nil {\n\t\tverifyCacheEvict(runAsUser)\n\t\treturn fmt.Errorf(\"passwordless sudo to user %q failed (check that your sudoers rule is present and scoped to this user): %w: %s\", runAsUser, err, strings.TrimSpace(string(out)))\n\t}\n\tout, err := runner.Run(ctx, \"-n\", \"-iu\", runAsUser, \"--\", \"sudo\", \"-n\", \"/usr/bin/true\")\n\tif err == nil {\n\t\tverifyCacheEvict(runAsUser)\n\t\treturn fmt.Errorf(\"target user %q can run passwordless sudo; isolation is meaningless. Remove NOPASSWD sudo for this user. Output: %s\", runAsUser, strings.TrimSpace(string(out)))\n\t}\n\tverifyCacheStore(runAsUser)\n\treturn nil\n}\n\n// verifyCacheTTL is short by design. It absorbs a burst of messages","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/runas.go#L219-L255","documentation":"VerifyRunAsUserCheap performs fast, cheap validation that the configured run_as_user is usable via sudo (passwordless sudo -iu to run /usr/bin/true). It throws this error synchronously when the runAsUser argument is an empty string, since sudo validation cannot proceed without a target user. This is an input-invariant check before the sudo probe.","triggerScenarios":"newClaudeSession calls VerifyRunAsUserCheap with runAsUser == \"\" — i.e. the agent/session config yielded no run_as_user value, or an empty string was passed explicitly.","commonSituations":"config.toml has run_as_user = \"\" or the key is missing while a code path still invokes the sudo validation; a migration renamed the config field leaving the value empty; programmatic construction of session options omitted RunAsUser.","solutions":["Set a non-empty run_as_user in config.toml for the project/agent.","Guard the call: only invoke VerifyRunAsUserCheap when run_as_user is configured (empty means the feature is off).","Run `cc-connect doctor user-isolation` to validate the full run_as_user setup."],"exampleFix":"// before\nif err := core.VerifyRunAsUserCheap(ctx, runner, cfg.RunAsUser); err != nil {\n    return err\n}\n// after\nif cfg.RunAsUser == \"\" {\n    return nil // run_as_user disabled; skip sudo verification\n}\nif err := core.VerifyRunAsUserCheap(ctx, runner, cfg.RunAsUser); err != nil {\n    return fmt.Errorf(\"claudecode: verify run_as_user: %w\", err)\n}","handlingStrategy":"validation","validationCode":"if runAsUser == \"\" {\n    return nil // feature disabled, skip verification\n}","typeGuard":null,"tryCatchPattern":"if err := core.VerifyRunAsUserCheap(ctx, runner, user); err != nil {\n    return fmt.Errorf(\"run_as_user verification failed: %w\", err)\n}","preventionTips":["Treat empty run_as_user as 'feature off' and short-circuit before calling.","Validate config at load time: reject run_as_user keys that parse to empty strings when isolation is expected.","Run cc-connect doctor user-isolation after config changes."],"tags":["config","sudo","isolation","empty-argument"],"backgroundTag":"missing-required-argument","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"}