Hmbown/CodeWhale · error · io::Error
Codewhale-owned credential file DACL must grant only one…
Error message
Codewhale-owned credential file DACL must grant only one user
What it means
The library enumerates the DACL with GetExplicitEntriesFromAclW and requires exactly one explicit access entry; anything else (zero entries, or more than one grantee) means some principal other than the single intended user may hold access, so it throws PermissionDenied. This completes the owner-only policy: one owner, one ACE, no inheritance residue.
Solutions
- Reset to a single user ACE: `icacls <file> /inheritance:r /grant:r "%USERNAME%:F"` (this drops all other ACEs).
- Remove individual extra grantees: `icacls <file> /remove:g Users "Authenticated Users"` until only your account remains.
- Inspect with `icacls <file>` or `Get-Acl <file> | Format-List` to see exactly who is granted access.
- Avoid GUI sharing dialogs on the credential file; they add extra ACEs.
Example fix
:: before icacls C:\Users\me\.codewhale\token.json :: grants me AND BUILTIN\Users :: after icacls C:\Users\me\.codewhale\token.json /inheritance:r /grant:r "%USERNAME%:F"
Defensive patterns
Strategy: validation
Validate before calling
// PowerShell pre-check: exactly one user should appear in the ACL // (Get-Acl $path).Access | Select-Object IdentityReference, AccessControlType
Try / catch
match read_codewhale_owned_to_string(&path) {
Ok(creds) => use(creds),
Err(e) if e.to_string().contains("grant only one user") => {
eprintln!("reset ACL: icacls {p} /inheritance:r /grant:r %USERNAME%:F", p = path.display());
}
Err(e) => return Err(e),
} Prevention
- Grant access to exactly one account; avoid GUI sharing dialogs on credential files.
- Use /inheritance:r to drop inherited group grants when creating the file.
- After any icacls change, re-list with `icacls <file>` to confirm a single ACE.
- Audit ACLs periodically in environments with group policy folder redirection.
When it happens
Trigger: read_codewhale_owned_to_string opens a file whose DACL has zero explicit entries or multiple ACEs — e.g. grants to both the user and Administrators/Users, or leftover inherited-style entries flattened onto the file.
Common situations: `icacls /grant` was run twice for different accounts; a sharing wizard granted Users or Authenticated Users read access; the file kept ACEs after a user/profile rename; group policies or folder inheritance added extra grants.
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
- Codewhale-owned credential file must have an owner-only DACL
- Codewhale-owned credential file owner is not the current…
- Codewhale-owned xAI OAuth DACL must grant only one user
- Codewhale-owned xAI OAuth storage must have an owner-only…
- Codewhale issue-report DACL is not current-user-only
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/0b9db43d1e3840f6.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/external_credentials.rs:490
));
}
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));
}
let _entries = WindowsLocalAllocation(entries.cast());
if count != 1 || entries.is_null() {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"Codewhale-owned credential file DACL must grant only one user",
));
}
// SAFETY: `count == 1` proves the first returned entry is initialized.
let entry = unsafe { &*entries };
let trustee_sid: PSID = entry.Trustee.ptstrName.cast();
// SAFETY: form and null checked in this expression; sid owned by `user`.
let current_user_only = entry.Trustee.TrusteeForm == TRUSTEE_IS_SID
&& !trustee_sid.is_null()
&& unsafe { EqualSid(trustee_sid, user.sid()) } != 0
&& matches!(entry.grfAccessMode, SET_ACCESS | GRANT_ACCESS)
&& entry.grfAccessPermissions == FILE_ALL_ACCESS;
if !current_user_only {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"Codewhale-owned credential file DACL is not current-user-only",
));View on GitHub (pinned to 73e0f67d83)