astrid-runtime/astrid · error
named-pipe client's effective token belongs to a different o
Error message
named-pipe client's effective token belongs to a different operating-system user
What it means
During accept, the server impersonates the named-pipe client (ImpersonateNamedPipeClient) and reads the effective token's user SID; if it differs from the server's own user SID the client is rejected. This checks the identity the client is actually exercising on the pipe, complementing the process-owner check.
Source
Thrown at crates/astrid-core/src/local_transport/windows.rs:473
fn require_current_user_process_peer(stream: &LocalStream) -> io::Result<VerifiedPeerProcess> {
let peer = peer_process_identity(stream)?;
if peer.user_sid.equals(¤t_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(¤t_user_sid()?) {
Ok(())
} else {
Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"named-pipe client's effective token belongs to a different operating-system user",
))
}
}
fn effective_client_user_sid(stream: &LocalStream) -> io::Result<OwnedSid> {
let server = match &stream.inner {
StreamInner::Server(server) => server,
StreamInner::Client(_) => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"client effective-token validation requires the server pipe end",
));
},
};
let impersonated = unsafe { ImpersonateNamedPipeClient(server.as_raw_handle().cast()) };
if impersonated == 0 {View on GitHub (pinned to affd8760f4)
Solutions
- Start the client under the same user account that owns the server pipe.
- Check the client isn't impersonating another user before ConnectNamedPipe/CreateFile on the pipe.
- Inspect both accounts with `whoami /user` on each side to confirm the SIDs match.
Defensive patterns
Strategy: validation
Validate before calling
// on the client: ensure the process token owner matches the server account // e.g. verify with GetTokenInformation(TokenOwner) before opening the pipe
Type guard
fn token_owner_matches(client_sid: &Sid, server_sid: &Sid) -> bool {
client_sid.equals(server_sid)
} Try / catch
match listener.accept() {
Err(e) if e.kind() == io::ErrorKind::PermissionDenied
&& e.to_string().contains("effective token") => {
eprintln!("client token owner mismatch; dropping connection");
}
other => other?,
} Prevention
- Don't run clients under scheduled-task or service accounts that differ from the server user.
- Avoid impersonation on the client before connecting.
- Log both SIDs on rejection to diagnose account mismatches quickly.
When it happens
Trigger: accept() called on a server pipe whose connected client's effective security token belongs to a different OS user — e.g. the client connected using a token of another account, or its impersonation token differs from its process owner.
Common situations: Client running under a service account while the server runs as the logged-in user; client started via scheduled task under a different identity; duplicated/impersonated tokens on the client side.
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 peer process changed during authentication
- named-pipe peer process belongs to a different operating-sys
- named-pipe client disconnected before transport authenticati
- named-pipe peer PID did not identify the opened process
- named-pipe endpoint path must not contain a parent component
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/0c1a16823d17a588.
Report an issue: GitHub.