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

  1. Recreate the pipe using this library's creation API, which installs an explicit protected DACL for the current user and Local System.
  2. 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.
  3. 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.
  4. 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

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


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/a0d583860163bd5c. Report an issue: GitHub.