Hmbown/CodeWhale · error

Codewhale-owned xAI OAuth path must not be a reparse point

Error message

Codewhale-owned xAI OAuth path must not be a reparse point

What it means

On Windows, validate_windows_handle_path inspects the opened handle's file attributes and refuses any path with FILE_ATTRIBUTE_REPARSE_POINT set. Reparse points (symlinks, junctions, mount points) could redirect the credential path to attacker-controlled storage, so a Codewhale-owned path that resolves to a reparse point is rejected.

Solutions

  1. Remove the symlink/junction and move the real directory back to its normal location (e.g. %USERPROFILE%\.codewhale)
  2. Copy (not link) the directory contents to a physical path and update the config to the real location
  3. Disable folder redirection/sync for the credentials directory

Example fix

// before
mklink /J C:\Users\me\.codewhale D:\sync\codewhale
// after
robocopy D:\sync\codewhale C:\Users\me\.codewhale /E
rmdir C:\Users\me\.codewhale_link_placeholder
Defensive patterns

Strategy: validation

Validate before calling

use std::os::windows::fs::MetadataExt;
fn is_reparse_point(p: &std::path::Path) -> std::io::Result<bool> {
    Ok(std::fs::metadata(p)?.file_attributes() & 0x400 != 0) // FILE_ATTRIBUTE_REPARSE_POINT
}

Try / catch

if is_reparse_point(&dir)? {
    eprintln!("{} is a symlink/junction; move it to a real path", dir.display());
    return Err(anyhow::anyhow!("credentials path must not be a reparse point"));
}
let store = XaiCredentialStore::open(&dir)?;

Prevention

When it happens

Trigger: Opening the credentials directory or file when any component (or the leaf) is a symlink/junction — e.g. `mklink /J` on ~/.codewhale, OneDrive/Dropbox folder redirection, or a user profile symlink.

Common situations: Redirecting the .codewhale folder into a synced drive (OneDrive/Dropbox junctions); moving credentials to another drive with a junction; per-user profile folder redirection policies.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/8bad98f61e5bde18. Report an issue: GitHub.

Appendix: source

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

    expected: &Path,
    expect_directory: bool,
) -> Result<fs::Metadata> {
    use std::ffi::OsString;
    use std::os::windows::ffi::OsStringExt as _;
    use std::os::windows::fs::MetadataExt as _;
    use std::os::windows::io::AsRawHandle as _;
    use windows_sys::Win32::Storage::FileSystem::{
        FILE_ATTRIBUTE_REPARSE_POINT, FILE_NAME_NORMALIZED, GetFinalPathNameByHandleW,
        VOLUME_NAME_DOS,
    };

    let metadata = file.metadata().with_context(|| {
        format!(
            "inspecting Codewhale-owned path {}",
            crate::quote_os_path(expected)
        )
    })?;
    anyhow::ensure!(
        metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT == 0,
        "Codewhale-owned xAI OAuth path must not be a reparse point"
    );
    anyhow::ensure!(
        if expect_directory {
            metadata.is_dir()
        } else {
            metadata.is_file()
        },
        "Codewhale-owned xAI OAuth path has the wrong filesystem type"
    );
    let flags = FILE_NAME_NORMALIZED | VOLUME_NAME_DOS;
    let handle = file.as_raw_handle();
    // SAFETY: null output asks only for the required UTF-16 length.
    let needed = unsafe { GetFinalPathNameByHandleW(handle, std::ptr::null_mut(), 0, flags) };
    if needed == 0 {
        return Err(std::io::Error::last_os_error())
            .context("resolving Codewhale-owned xAI OAuth handle path");

View on GitHub (pinned to 73e0f67d83)