Hmbown/CodeWhale · error · io::Error

Codewhale-owned credential file owner is not the current…

Error message

Codewhale-owned credential file owner is not the current user

What it means

verify_windows_owner_only_handle retrieves the security descriptor of the opened credential handle and requires the file's owner SID to equal the current user's SID (via EqualSid). It throws PermissionDenied when the owner is null or a different account, ensuring the Codewhale-owned credential is not owned by another principal such as Administrator, SYSTEM, or another user.

Solutions

  1. Take ownership as the current user: `takeown /F <file>` then `icacls <file> /setowner %USERNAME%`, or reset ownership in the Security tab (Advanced → Owner).
  2. Recreate the file while running as the normal (non-elevated) user so it inherits correct ownership.
  3. If the app should run under a service account, create the credential under that account instead of copying it across accounts.
  4. Avoid provisioning credential files from elevated installers without fixing ownership afterwards.

Example fix

:: before
(run as admin) copy D:\secrets\token.json C:\Users\me\.codewhale\token.json   :: owner = Administrators
:: after
takeown /F C:\Users\me\.codewhale\token.json
icacls C:\Users\me\.codewhale\token.json /setowner %USERNAME%
Defensive patterns

Strategy: validation

Validate before calling

// PowerShell pre-check:
// (Get-Acl $path).Owner -eq [System.Security.Principal.WindowsIdentity]::GetCurrent().Name

Try / catch

match read_codewhale_owned_to_string(&path) {
    Ok(creds) => use(creds),
    Err(e) if e.to_string().contains("owner is not the current user") => {
        eprintln!("fix owner: takeown /F {p} && icacls {p} /setowner %USERNAME%", p = path.display());
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: read_codewhale_owned_to_string opens a file owned by a different account — e.g. created by an elevated installer running as Administrator, copied by another user, or restored from a backup preserving foreign ownership.

Common situations: Credential file provisioned by a setup script run with elevation, leaving Administrator/SYSTEM as owner; file copied from another machine/user profile; migration tool reset ownership; running the app under a different account than the one that created the file.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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

Appendix: source

Thrown at crates/tui/src/external_credentials.rs:469

    let result = unsafe {
        GetSecurityInfo(
            handle,
            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(io::Error::from_raw_os_error(result as i32));
    }
    let _descriptor = WindowsLocalAllocation(descriptor.cast());
    // SAFETY: `owner` is non-null; `user.sid()` is owned by `user`.
    if owner.is_null() || unsafe { EqualSid(owner, user.sid()) } == 0 {
        return Err(io::Error::new(
            io::ErrorKind::PermissionDenied,
            "Codewhale-owned credential file owner is not the current user",
        ));
    }
    if dacl.is_null() {
        return Err(io::Error::new(
            io::ErrorKind::PermissionDenied,
            "Codewhale-owned credential file must have an owner-only DACL",
        ));
    }
    let mut count = 0;
    let mut entries: *mut EXPLICIT_ACCESS_W = std::ptr::null_mut();
    // SAFETY: `dacl` is owned by the live security descriptor; Windows
    // allocates the returned entries, released below.
    let result = unsafe { GetExplicitEntriesFromAclW(dacl, &mut count, &mut entries) };
    if result != ERROR_SUCCESS {
        return Err(io::Error::from_raw_os_error(result as i32));
    }

View on GitHub (pinned to 73e0f67d83)