astrid-runtime/astrid · error
named-pipe peer process changed during authentication
Error message
named-pipe peer process changed during authentication
What it means
During authentication the library re-checks that the pipe's peer PID still matches the PID of the process that was verified earlier (VerifiedPeerProcess::ensure_still_peer). If GetNamedPipeClientProcessId now returns a different PID, the original authenticated process disconnected and a different process connected on the same pipe instance, so the prior identity verification is stale and the connection is rejected with PermissionDenied.
Source
Thrown at crates/astrid-core/src/local_transport/windows.rs:420
));
}
Ok(process_id)
}
struct VerifiedPeerProcess {
process_id: u32,
user_sid: OwnedSid,
// Keeping the process object alive prevents its numeric PID from being
// recycled while security validation runs. We still re-read the PID from
// the pipe afterward; this is defense in depth beside the descriptor owner
// check, not the server's effective-token authorization boundary.
_process: OwnedHandle,
}
impl VerifiedPeerProcess {
fn ensure_still_peer(&self, stream: &LocalStream) -> io::Result<()> {
if peer_process_id(stream)? != self.process_id {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"named-pipe peer process changed during authentication",
));
}
Ok(())
}
}
fn peer_process_identity(stream: &LocalStream) -> io::Result<VerifiedPeerProcess> {
let process_id = peer_process_id(stream)?;
let process = unsafe { OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, process_id) };
if process.is_null() {
return Err(last_error("failed to open named-pipe peer process"));
}
let process = OwnedHandle(process);
if unsafe { GetProcessId(process.0) } != process_id {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,View on GitHub (pinned to affd8760f4)
Solutions
- Retry the whole connect/accept handshake from scratch — the identity must be re-verified for the new peer.
- Ensure clients hold the connection for the full auth lifetime (no close/reopen between auth steps).
- On the server, recreate the pipe instance after this error so a stale peer cannot reuse it.
Example fix
// before
match peer.ensure_still_peer(&stream) {
Err(_) => return Err(err),
}
// after
match peer.ensure_still_peer(&stream) {
Err(e) if e.kind() == io::ErrorKind::PermissionDenied => {
drop(stream);
return connect_fresh(&path); // re-run full authentication
}
Err(e) => return Err(e),
Ok(()) => {}
} Defensive patterns
Strategy: try-catch
Try / catch
if let Err(e) = peer.ensure_still_peer(&stream) {
if e.kind() == io::ErrorKind::PermissionDenied {
// peer changed mid-auth: restart the full handshake
return authenticate_fresh(&path);
}
return Err(e);
} Prevention
- Never close/reopen the pipe between identity verification and use.
- Avoid client retry loops that instantly reconnect on the same instance.
- Run an integration test with a client that disconnects mid-handshake.
When it happens
Trigger: A client process exits mid-handshake and another process opens the same pipe instance before the server finishes ensure_still_peer; racing reconnects against a permissive first-instance pipe.
Common situations: Aggressive client retry loops that reconnect immediately after a crash; service supervisors restarting workers that share one pipe server; TOCTOU-style hijack attempts on local IPC.
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.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- named-pipe peer process belongs to a different operating-sys
- named-pipe client's effective token belongs to a different o
- 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/2a2c81c92d0ee6ae.
Report an issue: GitHub.