astrid-runtime/astrid · error
named-pipe owner is not the current operating-system user
Error message
named-pipe owner is not the current operating-system user
What it means
During connect/accept on Windows, the library reads the named pipe's security descriptor via GetSecurityInfo and requires the pipe's owner SID to equal the SID of the current operating-system user. This error means the pipe is owned by a different user (or the owner could not be resolved), so the client refuses to talk to a pipe it does not own — a defense against symlink/squatting attacks where an attacker pre-creates the pipe. The failure surfaces as io::Error with ErrorKind::PermissionDenied from validate_pipe_security.
Source
Thrown at crates/astrid-core/src/local_transport/windows.rs:917
};
if status != 0 {
return Err(io::Error::from_raw_os_error(
i32::try_from(status).unwrap_or(i32::MAX),
));
}
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,View on GitHub (pinned to affd8760f4)
Solutions
- Recreate the pipe from the same OS user account that the current process runs as (the library's pipe creation path sets the owner correctly).
- Check who owns the pipe with `Get-Acl \\.\pipe\<name>` in PowerShell; if it is a stale pipe from another account, close that process or delete the pipe endpoint.
- Verify you are not mixing elevated and non-elevated sessions; run both creator and consumer under the same user, or recreate the pipe after switching accounts.
- If the pipe path is derived from config, ensure it points at your own instance's pipe, not another user's.
Example fix
// before: pipe created by an elevated shell, client runs unelevated -> owner mismatch // after: create the pipe in the same session/user as the client let transport = LocalTransport::connect(&pipe_path)?; // pipe must be (re)created by current user // e.g. start the server without 'Run as administrator', or start the client // with the same account that owns the pipe
Defensive patterns
Strategy: validation
Validate before calling
// Before connecting, check the pipe's owner matches the current user (PowerShell preflight or in-process):
// powershell: (Get-Acl \\.\pipe\myapp).Owner -eq "<DOMAIN>\\<user>"
let expected = whoami::username(); // or query token via GetTokenInformation
if !pipe_owner_matches_current_user(&pipe_path) {
eprintln!("pipe {pipe_path} is not owned by {expected}; recreate it");
} Try / catch
match std::fs::metadata? -> no; use: match connect() {
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
// owner mismatch: recreate pipe under current user or surface clear guidance
recreate_pipe_as_current_user()?;
connect()?;
}
r => r?,
} Prevention
- Run both pipe creator and consumer under the same OS user account
- Avoid mixing elevated and non-elevated sessions for the same pipe
- Use unique per-user pipe names (include the username) to avoid squatting
- Check pipe ownership with Get-Acl when diagnosing connection failures
When it happens
Trigger: Calling connect() or accept() on a named pipe whose security descriptor owner SID does not match the current user's token SID (EqualSid returns 0), or when the descriptor has a null owner pointer.
Common situations: The pipe was created by a service or another user account (e.g. created earlier under an elevated/admin session, or a scheduled task) and the current process runs as a different account; an attacker planted a pipe at the expected path; running under runas/sudo-like account switching so the connecting user differs from the creator.
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 has a null or missing DACL
- 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
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/e4e5d78f2e127989.
Report an issue: GitHub.