astrid-runtime/astrid · error
named-pipe has a null or missing DACL
Error message
named-pipe has a null or missing DACL
What it means
validate_pipe_security (used by connect and accept) fetched the pipe's security descriptor and found the DACL pointer is null — the pipe has no discretionary access-control list. The library requires an explicit DACL so it can prove only the current user and Local System can access the pipe; a null DACL means everyone could be allowed access, so it fails closed with PermissionDenied.
Source
Thrown at crates/astrid-core/src/local_transport/windows.rs:923
if descriptor.is_null() {
return Err(io::Error::other(
"Windows returned no named-pipe security descriptor",
));
}
let descriptor_allocation = LocalAllocation(descriptor);
// SAFETY: GetSecurityInfo returned this non-null descriptor, and
// `descriptor_allocation` keeps it live through validation.
unsafe { validate_descriptor_control(descriptor) }?;
if owner.is_null() || unsafe { EqualSid(owner, current.as_psid()) } == 0 {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"named-pipe owner is not the current operating-system user",
));
}
if dacl.is_null() {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"named-pipe has a null or missing DACL",
));
}
// SAFETY: `dacl` points into the descriptor allocation returned by
// GetSecurityInfo, which remains live and unmodified through
// `descriptor_allocation`. The parser validates and bounds the ACL before
// exposing any borrowed ACE or SID.
let dacl = unsafe {
ValidatedAcl::from_raw(
dacl,
&descriptor_allocation,
"named-pipe security descriptor",
)
}?;
let expected_aces = if current.equals(&system) { 1 } else { 2 };
let ace_count = dacl.ace_count();View on GitHub (pinned to affd8760f4)
Solutions
- Recreate the pipe using this library's creation API, which installs an explicit protected DACL for the current user and Local System.
- Inspect the existing pipe's ACL (`Get-Acl \\.\pipe\<name>`) to identify who created it, then stop that process or use a different pipe name.
- If you create the pipe yourself with raw CreateNamedPipe, pass a SECURITY_ATTRIBUTES with an explicit DACL (SDDL like `D:P(A;;GA;;;current-sid)(A;;GA;;;SY)`) instead of NULL.
- Confirm no other tool or wrapper replaced the pipe at that name.
Example fix
// before: creating the pipe without security attributes
CreateNamedPipeW(path, ..., std::ptr::null_mut(), ...);
// after: supply SECURITY_ATTRIBUTES with a protected DACL
let sa = SECURITY_ATTRIBUTES { nLength: size_of::<SECURITY_ATTRIBUTES>() as u32, lpSecurityDescriptor: build_protected_dacl_descriptor(), bInheritHandle: 0 };
CreateNamedPipeW(path, ..., &sa, ...); Defensive patterns
Strategy: validation
Validate before calling
// Preflight: ensure the pipe has an explicit DACL before connecting
// powershell: if (-not (Get-Acl \\.\pipe\myapp).AreAccessRulesProtected) { ... }
// or in Rust, call GetNamedPipeHandleState/GetSecurityInfo yourself and check dacl.is_some() Try / catch
match connect() {
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied && e.to_string().contains("null or missing DACL") => {
eprintln!("pipe has no DACL; it was not created by this library — recreate it");
}
r => r?,
} Prevention
- Always create the pipe through the library's own creation API
- Never pass NULL SECURITY_ATTRIBUTES when creating pipes with raw CreateNamedPipe
- Verify pipe ACLs with Get-Acl when connecting to externally created pipes
- Use unique pipe names so a foreign process cannot replace your endpoint
When it happens
Trigger: connect()/accept() on a pipe whose SECURITY_DESCRIPTOR has SE_DACL_PRESENT unset or a null ACL pointer, typically because the pipe was created with a NULL/no security attributes or SECURITY_DACL_PRESENT omitted.
Common situations: A third-party program or older library version created the pipe with default/null security attributes; someone recreated the pipe manually with `CreateNamedPipe` passing lpSecurityAttributes = NULL with permissive defaults; the pipe at the expected path is a different, foreign pipe.
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
- named-pipe DACL has {ace_count} entries; expected exactly {e
- named-pipe DACL contains a non-canonical access entry
- named-pipe DACL grants an unexpected or duplicate principal
- named-pipe DACL omits the current user or Local System
- named-pipe DACL control is not explicit and protected (contr
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/a0d583860163bd5c.
Report an issue: GitHub.