Hmbown/CodeWhale · error
Codewhale issue-report storage must have an owner-only DACL
Error message
Codewhale issue-report storage must have an owner-only DACL
What it means
As part of the same Windows hardening pass, `verify_windows_owner_only_handle` requires the storage's security descriptor to carry a DACL, and the surrounding code requires it to be an owner-only DACL. `anyhow::ensure!(!dacl.is_null(), ...)` throws when the descriptor has no DACL at all. Without a DACL the storage's access policy is undefined/inheritable, defeating the owner-only isolation guarantee.
Solutions
- Delete the storage directory and let Codewhale recreate it with the correct owner-only DACL (cleanest fix).
- Restore an owner-only DACL manually: `icacls <storage-path> /inheritance:r /grant:r "%USERNAME%:F"` and apply to children.
- Re-run the failing operation after fixing the ACL; verification reads a fresh descriptor each time.
- Avoid copying the storage with tools that do not preserve ACLs; recreate instead of migrate.
Example fix
// before # DACL stripped by sync tool # after (cmd) icacls "%LOCALAPPDATA%\codewhale\issue-reports" /inheritance:r /grant:r "%USERNAME%:F" /t
Defensive patterns
Strategy: validation
Validate before calling
# PowerShell pre-check that the storage has a DACL
$p = "$env:LOCALAPPDATA\codewhale\issue-reports"
if (Test-Path $p) {
$acl = Get-Acl $p
if ($acl.Access.Count -eq 0) {
Write-Error "no DACL entries; run: icacls $p /inheritance:r /grant:r `"$env:USERNAME`:F`" /t"
}
} Type guard
fn storage_has_dacl(path: &std::path::Path) -> bool {
std::process::Command::new("icacls")
.arg(path)
.output()
.map(|o| o.status.success() && !String::from_utf8_lossy(&o.stdout).trim().is_empty())
.unwrap_or(false)
} Try / catch
match report_tool.verify_and_open() {
Ok(handle) => handle,
Err(e) if e.to_string().contains("must have an owner-only DACL") => {
// DACL missing/stripped: recreate storage with correct ACLs
std::fs::remove_dir_all(storage_path)?;
report_tool.verify_and_open()
}
Err(e) => return Err(e),
} Prevention
- Do not copy or move the storage with tools that drop ACLs; delete and recreate instead.
- Avoid 'reset permissions' / 'replace child permissions' operations over the storage path.
- Keep the storage on an NTFS volume with normal inheritance rather than exotic filesystems.
- Re-verify ACLs after restores or sync-tool runs touching the config area.
When it happens
Trigger: Calling the issue-report tool on Windows when the storage's security descriptor lacks a DACL — typically because the file was created without explicit ACLs on a volume without inheritance, by a tool that copied the object but dropped the DACL, or after an ACL was cleared with `icacls /remove`.
Common situations: A backup/restore or file-sync tool stripped the DACL; the storage was moved between volumes with different inheritance semantics; an admin reset permissions with 'replace all child permissions' leaving a null DACL.
Understand the failure class
Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.
Related errors
- Codewhale issue-report DACL is not current-user-only
- Codewhale issue-report DACL must grant only one user
- Codewhale-owned credential file DACL must grant only one…
- Codewhale-owned credential file must have an owner-only DACL
- Codewhale-owned credential file owner is not the current…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/fbe3cf7d007f2229.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/github/report.rs:1077
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 issue-report security descriptor");
}
let _descriptor = WindowsLocalAllocation(descriptor.cast());
anyhow::ensure!(
!owner.is_null() && unsafe { EqualSid(owner, user.sid()) } != 0,
"Codewhale issue-report storage owner is not the current user"
);
anyhow::ensure!(
!dacl.is_null(),
"Codewhale issue-report 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 issue-report DACL entries");
}
let _entries = WindowsLocalAllocation(entries.cast());
anyhow::ensure!(
count == 1 && !entries.is_null(),
"Codewhale issue-report DACL must grant only one user"
);
// SAFETY: `count == 1` proves the first returned entry is initialized.View on GitHub (pinned to 73e0f67d83)