astrid-runtime/astrid · error

Windows named-pipe endpoint denied access

Error message

Windows named-pipe endpoint denied access

What it means

ERROR_ACCESS_DENIED from CreateFile is re-mapped to io::ErrorKind::PermissionDenied with this message: the pipe exists, but the caller is not allowed to open it. Since the library scopes pipe names per user SID, this usually indicates the pipe's security descriptor or the per-user naming rejected the caller's identity.

Source

Thrown at crates/astrid-core/src/local_transport/windows.rs:668

        Some(ERROR_FILE_NOT_FOUND) => Ok(EndpointState::Absent),
        Some(ERROR_PIPE_BUSY | ERROR_SEM_TIMEOUT | ERROR_ACCESS_DENIED) => {
            Ok(EndpointState::BusyOrDenied)
        },
        _ => Err(error),
    }
}

fn classify_connect_error(error: io::Error) -> io::Error {
    match error.raw_os_error().map(i32::cast_unsigned) {
        Some(ERROR_FILE_NOT_FOUND) => io::Error::new(
            io::ErrorKind::NotFound,
            "Windows named-pipe endpoint is absent",
        ),
        Some(ERROR_PIPE_BUSY) => io::Error::new(
            io::ErrorKind::WouldBlock,
            "Windows named-pipe endpoint is busy",
        ),
        Some(ERROR_ACCESS_DENIED) => io::Error::new(
            io::ErrorKind::PermissionDenied,
            "Windows named-pipe endpoint denied access",
        ),
        _ => error,
    }
}

struct PipeSecurity {
    _descriptor: LocalAllocation,
    attributes: SECURITY_ATTRIBUTES,
}

impl PipeSecurity {
    fn for_current_user() -> io::Result<Self> {
        let user = current_user_sid()?;
        let system = well_known_sid(WinLocalSystemSid)?;
        let user_sddl = user.to_sddl()?;
        let dacl = if user.equals(&system) {

View on GitHub (pinned to affd8760f4)

Solutions

  1. Run the client under the same Windows user account as the server — the pipe name includes the owner's SID, and access is same-user by design.
  2. Check for sandboxing/integrity-level differences (AppContainer, low integrity) that strip pipe access, and run the client unsandboxed.
  3. Verify no security software or policy is blocking named-pipe opens; compare `whoami /user` output on both sides.
Defensive patterns

Strategy: validation

Validate before calling

// confirm same-user and non-sandboxed context before connecting
// e.g. compare current_user_sid() to the server account; check integrity level

Type guard

fn client_can_open_pipe(current_sid: &Sid, server_sid: &Sid) -> bool {
    current_sid.equals(server_sid)
}

Try / catch

match transport.connect(&path) {
    Err(e) if e.kind() == io::ErrorKind::PermissionDenied
        && e.to_string().contains("denied access") => {
        eprintln!("pipe ACL/identity rejected us; check user account and sandboxing");
    }
    other => other?,
}

Prevention

When it happens

Trigger: connect() from a process whose token lacks access to the pipe (different user, low-integrity/AppContainer process, restricted token); connecting to a pipe created by another account whose per-user name coincidentally matches; DACL on the pipe instance denies the client.

Common situations: Client running as a service account against a user's pipe; sandboxed (AppContainer/Edge-style) clients; group-policy or AV software restricting named-pipe access.

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/bc58d47ab34a0587. Report an issue: GitHub.