{"record":{"id":"e26ed09e9c68bc01","repo":"Hmbown/CodeWhale","slug":"codewhale-credentials-directory-must-be-owned-by-t","errorCode":null,"errorMessage":"Codewhale credentials directory must be owned by the current user","messagePattern":"Codewhale credentials directory must be owned by the current user","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/config/src/xai_credentials.rs","lineNumber":540,"sourceCode":"                    \"opening Codewhale credentials directory without following links: {}\",\n                    crate::quote_os_path(directory)\n                )\n            });\n        }\n        // SAFETY: `fd` is a newly owned descriptor on the success path above.\n        current = unsafe { File::from_raw_fd(fd) };\n    }\n    let metadata = current.metadata().with_context(|| {\n        format!(\n            \"inspecting Codewhale credentials directory {}\",\n            crate::quote_os_path(directory)\n        )\n    })?;\n    anyhow::ensure!(\n        metadata.is_dir(),\n        \"Codewhale credentials path must be a directory\"\n    );\n    anyhow::ensure!(\n        metadata.uid() == unsafe { libc::geteuid() },\n        \"Codewhale credentials directory must be owned by the current user\"\n    );\n    current\n        .set_permissions(fs::Permissions::from_mode(0o700))\n        .with_context(|| {\n            format!(\n                \"securing Codewhale credentials directory {}\",\n                crate::quote_os_path(directory)\n            )\n        })?;\n    Ok(XaiOAuthCredentialStore {\n        directory: directory.to_path_buf(),\n        directory_handle: current,\n    })\n}\n\n#[cfg(unix)]","sourceCodeStart":522,"sourceCodeEnd":558,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/config/src/xai_credentials.rs#L522-L558","documentation":"The unix opener requires the pinned credentials directory uid to equal the effective uid (geteuid). 0700 owner-only permissions are meaningless if another account owns the directory, so the store refuses to read or write OAuth tokens through a directory a different principal controls.","triggerScenarios":"Running codewhale once under sudo so root creates .codewhale/credentials, then running as the normal user; a home restore that preserved different uid values; NFS id-mapping or root-squash mismatches; containers where a bind-mounted volume is owned by a host uid different from the container uid.","commonSituations":"`sudo codewhale ...` first runs; docker/k8s volumes with mismatched uids; account migrations on multi-user machines; WSL/embedded filesystems with odd ownership mapping.","solutions":["Take ownership: sudo chown -R \"$(id -u):$(id -g)\" \"${CODEWHALE_HOME:-$HOME/.codewhale}\"","Or remove the store and re-authenticate: rm -rf \"${CODEWHALE_HOME:-$HOME/.codewhale}/credentials\" && codewhale auth xai-device","Never run codewhale under sudo; run it as the account that owns the home directory","In containers, match the volume uid to the container user (--user \"$(id -u):$(id -g)\")"],"exampleFix":"# before\nsudo codewhale auth xai-device   # credentials dir now owned by root\n\n# after\nsudo chown -R \"$(id -u):$(id -g)\" \"$HOME/.codewhale\"\ncodewhale auth xai-device","handlingStrategy":"validation","validationCode":"#[cfg(unix)]\nfn owned_by_current_user(p: &std::path::Path) -> std::io::Result<bool> {\n    use std::os::unix::fs::MetadataExt as _;\n    let euid = unsafe { libc::geteuid() };\n    Ok(std::fs::metadata(p)?.uid() == euid)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never run the first codewhale login under sudo","In containers, match the mounted volume uid with --user","After sudo mishaps, chown the whole CODEWHALE_HOME back to the login user"],"tags":["unix","permissions","security","filesystem","xai-oauth"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}