{"record":{"id":"7bb3a2f47b07794c","repo":"Hmbown/CodeWhale","slug":"xai-oauth-file-must-not-have-multiple-filesystem-l","errorCode":null,"errorMessage":"xAI OAuth file must not have multiple filesystem links","messagePattern":"xAI OAuth file must not have multiple filesystem links","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/config/src/xai_credentials.rs","lineNumber":773,"sourceCode":"        Ok(())\n    }\n}\n\n#[cfg(unix)]\nfn validate_owned_file_handle(file: &File, path: &Path) -> Result<fs::Metadata> {\n    use std::os::unix::fs::MetadataExt as _;\n    let metadata = file.metadata().with_context(|| {\n        format!(\n            \"inspecting Codewhale-owned xAI OAuth file {}\",\n            crate::quote_os_path(path)\n        )\n    })?;\n    anyhow::ensure!(metadata.is_file(), \"xAI OAuth path must be a regular file\");\n    anyhow::ensure!(\n        metadata.uid() == unsafe { libc::geteuid() },\n        \"xAI OAuth file must be owned by the current user\"\n    );\n    anyhow::ensure!(\n        metadata.nlink() == 1,\n        \"xAI OAuth file must not have multiple filesystem links\"\n    );\n    Ok(metadata)\n}\n\n#[cfg(windows)]\nfn open_owned_credentials_directory(directory: &Path) -> Result<XaiOAuthCredentialStore> {\n    use std::os::windows::fs::OpenOptionsExt as _;\n    use windows_sys::Win32::Storage::FileSystem::{\n        FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, FILE_GENERIC_READ,\n        FILE_SHARE_READ, FILE_SHARE_WRITE, WRITE_DAC, WRITE_OWNER,\n    };\n\n    anyhow::ensure!(\n        directory.is_absolute(),\n        \"xAI OAuth credentials directory must be absolute\"\n    );","sourceCodeStart":755,"sourceCodeEnd":791,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/config/src/xai_credentials.rs#L755-L791","documentation":"validate_owned_file_handle requires nlink == 1 on every unix credential file. A second hard link would keep the secret bytes readable after the store deletes its own link, defeating secure deletion, so the store fails closed instead of touching multi-linked files.","triggerScenarios":"Someone ran `ln ~/.codewhale/credentials/xai-auth.json ~/backup`; deduplicating backup tooling that hard-links files (rsync -H, some snapshot tools); leftover staging links from interrupted writes in older versions.","commonSituations":"Users 'backing up' their login with a hard link; home-directory dedup tools; interrupted first-generation writes.","solutions":["Locate extra links: find \"${CODEWHALE_HOME:-$HOME/.codewhale}/credentials\" -type f -links +1 -print and find / -samefile <path> 2>/dev/null, then delete the outside copies","Or simply remove the file and re-run codewhale auth xai-device so a fresh single-link file is written","Reconfigure backup tooling to copy (not hard-link) anything inside the credentials directory"],"exampleFix":"# before\nln \"$HOME/.codewhale/credentials/xai-auth.json\" ~/backup-auth\n\n# after\nrm ~/backup-auth   # drop the second link\nfind \"$HOME/.codewhale/credentials\" -type f -links +1   # should print nothing","handlingStrategy":"validation","validationCode":"#[cfg(unix)]\nfn has_single_link(p: &std::path::Path) -> std::io::Result<bool> {\n    use std::os::unix::fs::MetadataExt as _;\n    Ok(std::fs::symlink_metadata(p)?.nlink() == 1)\n}","typeGuard":"#[cfg(unix)]\nfn credential_has_single_link(dir: &std::path::Path, name: &str) -> bool {\n    use std::os::unix::fs::MetadataExt as _;\n    std::fs::symlink_metadata(dir.join(name))\n        .map(|m| m.nlink() == 1)\n        .unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Never create hard links to credential files","Exclude $CODEWHALE_HOME/credentials from deduplicating/hard-linking backups","Periodically check with: find \"$CODEWHALE_HOME/credentials\" -type f -links +1"],"tags":["unix","security","hard-link","filesystem","xai-oauth"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}