Hmbown/CodeWhale · error

Codewhale-owned xAI OAuth storage must have an owner-only DA

Error message

Codewhale-owned xAI OAuth storage must have an owner-only DACL

What it means

verify_windows_owner_only_handle requires a non-NULL DACL on every secured handle. A NULL DACL on Windows grants everyone full access, so the store refuses it outright. GetSecurityInfo returns a NULL DACL on filesystems without ACL support, most commonly FAT32/exFAT volumes.

Source

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

            SE_FILE_OBJECT,
            OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
            &mut owner,
            std::ptr::null_mut(),
            &mut dacl,
            std::ptr::null_mut(),
            &mut descriptor,
        )
    };
    if result != ERROR_SUCCESS {
        return Err(std::io::Error::from_raw_os_error(result as i32))
            .context("reading Codewhale-owned xAI OAuth security descriptor");
    }
    let _descriptor = WindowsLocalAllocation(descriptor.cast());
    anyhow::ensure!(
        !owner.is_null() && unsafe { EqualSid(owner, user.sid()) } != 0,
        "Codewhale-owned xAI OAuth storage owner is not the current user"
    );
    anyhow::ensure!(
        !dacl.is_null(),
        "Codewhale-owned xAI OAuth storage must have an owner-only DACL"
    );
    let mut count = 0;
    let mut entries: *mut EXPLICIT_ACCESS_W = std::ptr::null_mut();
    // SAFETY: `dacl` belongs to the live descriptor; Windows allocates the
    // returned entry array, released by the guard below.
    let result = unsafe { GetExplicitEntriesFromAclW(dacl, &mut count, &mut entries) };
    if result != ERROR_SUCCESS {
        return Err(std::io::Error::from_raw_os_error(result as i32))
            .context("reading Codewhale-owned xAI OAuth DACL entries");
    }
    let _entries = WindowsLocalAllocation(entries.cast());
    anyhow::ensure!(
        count == 1 && !entries.is_null(),
        "Codewhale-owned xAI OAuth DACL must grant only one user"
    );
    // SAFETY: `count == 1` proves the first returned entry is initialized.

View on GitHub (pinned to 8880682c63)

Solutions

  1. Move CODEWHALE_HOME to an NTFS volume (e.g. under the real user profile) and re-run codewhale auth xai-device
  2. Reformat the target volume as NTFS if it must host the home directory
  3. Do not copy old files over; authenticate again so the store creates properly secured storage

Example fix

:: before
set CODEWHALE_HOME=E:\codewhale   (E: is FAT32/exFAT)

:: after
set CODEWHALE_HOME=%USERPROFILE%\.codewhale
codewhale auth xai-device
Defensive patterns

Strategy: fallback

Try / catch

if let Err(e) = login() {
    if e.to_string().contains("must have an owner-only DACL") {
        // FAT/exFAT cannot store ACLs: fall back to an NTFS-backed home
        std::env::set_var("CODEWHALE_HOME", ntfs_home.display().to_string());
        return login();
    }
    return Err(e);
}

Prevention

When it happens

Trigger: $CODEWHALE_HOME redirected to a FAT32/exFAT USB or SD drive; any object whose DACL was removed by low-level tooling.

Common situations: Portable/USB home directories; exFAT-formatted secondary drives used for profiles; embedded volumes.

Related errors


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