astrid-runtime/astrid · error

named-pipe peer process belongs to a different operating-sys

Error message

named-pipe peer process belongs to a different operating-system user

What it means

require_current_user_process_peer compares the peer process's owner SID with the local process's user SID and rejects the connection when they differ. This enforces same-user-only IPC over named pipes, preventing other accounts (or privilege-escalating sessions) from talking to your pipe server.

Source

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

    let mut token = ptr::null_mut();
    let opened = unsafe { OpenProcessToken(process.0, TOKEN_QUERY, &raw mut token) };
    if opened == 0 || token.is_null() {
        return Err(last_error("failed to open named-pipe peer process token"));
    }
    let user_sid = token_user_sid(&OwnedHandle(token))?;
    Ok(VerifiedPeerProcess {
        process_id,
        user_sid,
        _process: process,
    })
}

fn require_current_user_process_peer(stream: &LocalStream) -> io::Result<VerifiedPeerProcess> {
    let peer = peer_process_identity(stream)?;
    if peer.user_sid.equals(&current_user_sid()?) {
        Ok(peer)
    } else {
        Err(io::Error::new(
            io::ErrorKind::PermissionDenied,
            "named-pipe peer process belongs to a different operating-system user",
        ))
    }
}

fn require_current_user_effective_client(stream: &LocalStream) -> io::Result<()> {
    let client_sid = effective_client_user_sid(stream)?;
    if client_sid.equals(&current_user_sid()?) {
        Ok(())
    } else {
        Err(io::Error::new(
            io::ErrorKind::PermissionDenied,
            "named-pipe client's effective token belongs to a different operating-system user",
        ))
    }
}

View on GitHub (pinned to affd8760f4)

Solutions

  1. Run the client and server under the same Windows user account.
  2. Avoid mixing elevation states — run both sides elevated or both non-elevated.
  3. If cross-user IPC is genuinely required, use a transport designed for it (e.g. TCP on localhost with explicit auth) instead of this same-user pipe.

Example fix

// before (client launched as another user)
Process.Start(new ProcessStartInfo { UserName = "svc", ... });
// after
// launch client as the same interactive user as the pipe server
Process.Start(new ProcessStartInfo { UseShellExecute = true, FileName = "client.exe" });
Defensive patterns

Strategy: validation

Validate before calling

// before connecting, compare SIDs on both sides via whoami /user
// or in Rust: assert current_user_sid() equals the account the client runs under

Type guard

fn same_user(peer_sid: &Sid, current: &Sid) -> bool { peer_sid.equals(current) }

Try / catch

match listener.accept() {
    Err(e) if e.kind() == io::ErrorKind::PermissionDenied
        && e.to_string().contains("different operating-system user") => {
        eprintln!("peer ran as another user; reject and keep listening");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling connect or accept where the client process runs as a different OS user (different account, service account vs. interactive user, elevated vs. standard session mapping to different SIDs).

Common situations: A Windows service (LocalSystem) hosting the pipe while a desktop user app connects; running the client under 'Run as administrator' with a split-token admin SID; connecting across user sessions (RDP vs. console).

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