Hmbown/CodeWhale · error

xAI OAuth file must not have multiple filesystem links

Error message

xAI OAuth file must not have multiple filesystem links

What it means

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.

Source

Thrown at crates/config/src/xai_credentials.rs:773

        Ok(())
    }
}

#[cfg(unix)]
fn validate_owned_file_handle(file: &File, path: &Path) -> Result<fs::Metadata> {
    use std::os::unix::fs::MetadataExt as _;
    let metadata = file.metadata().with_context(|| {
        format!(
            "inspecting Codewhale-owned xAI OAuth file {}",
            crate::quote_os_path(path)
        )
    })?;
    anyhow::ensure!(metadata.is_file(), "xAI OAuth path must be a regular file");
    anyhow::ensure!(
        metadata.uid() == unsafe { libc::geteuid() },
        "xAI OAuth file must be owned by the current user"
    );
    anyhow::ensure!(
        metadata.nlink() == 1,
        "xAI OAuth file must not have multiple filesystem links"
    );
    Ok(metadata)
}

#[cfg(windows)]
fn open_owned_credentials_directory(directory: &Path) -> Result<XaiOAuthCredentialStore> {
    use std::os::windows::fs::OpenOptionsExt as _;
    use windows_sys::Win32::Storage::FileSystem::{
        FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, FILE_GENERIC_READ,
        FILE_SHARE_READ, FILE_SHARE_WRITE, WRITE_DAC, WRITE_OWNER,
    };

    anyhow::ensure!(
        directory.is_absolute(),
        "xAI OAuth credentials directory must be absolute"
    );

View on GitHub (pinned to 8880682c63)

Solutions

  1. 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
  2. Or simply remove the file and re-run codewhale auth xai-device so a fresh single-link file is written
  3. Reconfigure backup tooling to copy (not hard-link) anything inside the credentials directory

Example fix

# before
ln "$HOME/.codewhale/credentials/xai-auth.json" ~/backup-auth

# after
rm ~/backup-auth   # drop the second link
find "$HOME/.codewhale/credentials" -type f -links +1   # should print nothing
Defensive patterns

Strategy: validation

Validate before calling

#[cfg(unix)]
fn has_single_link(p: &std::path::Path) -> std::io::Result<bool> {
    use std::os::unix::fs::MetadataExt as _;
    Ok(std::fs::symlink_metadata(p)?.nlink() == 1)
}

Type guard

#[cfg(unix)]
fn credential_has_single_link(dir: &std::path::Path, name: &str) -> bool {
    use std::os::unix::fs::MetadataExt as _;
    std::fs::symlink_metadata(dir.join(name))
        .map(|m| m.nlink() == 1)
        .unwrap_or(false)
}

Prevention

When it happens

Trigger: 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.

Common situations: Users 'backing up' their login with a hard link; home-directory dedup tools; interrupted first-generation writes.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/7bb3a2f47b07794c. Report an issue: GitHub.