astrid-runtime/astrid · error
client effective-token validation requires the server pipe…
Error message
client effective-token validation requires the server pipe end
What it means
effective_client_user_sid works by impersonating the client, which is only possible from the server end of a named pipe (it needs the server handle obtained from CreateNamedPipe). When handed a client-end LocalStream it returns InvalidInput, because effective-token validation is meaningless from the client side.
Solutions
- Only call effective-token validation on streams returned by accept() on the server side.
- On the client end, validate the server differently (e.g. process-owner checks) or skip effective-token checks entirely.
- Track which end of the pipe you hold; if you need the check, forward the server stream handle to the code performing validation.
Example fix
// before let stream = LocalTransport::connect(&path)?; let sid = peer_is_current_user(&stream)?; // InvalidInput // after let stream = listener.accept()?; // server end only let sid = peer_is_current_user(&stream)?;
Defensive patterns
Strategy: type-guard
Type guard
fn is_server_end(stream: &LocalStream) -> bool {
matches!(stream.inner, StreamInner::Server(_))
} Try / catch
if !is_server_end(&stream) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"effective-token check is only valid on the server pipe end",
));
} Prevention
- Only perform peer/effective-token validation on streams from accept().
- Wrap client and server streams in distinct newtypes so ends can't be confused.
- Centralize auth checks behind a server-side API instead of free functions.
When it happens
Trigger: Calling peer_is_current_user or require_current_user_effective_client on a LocalStream created by connect() (StreamInner::Client) rather than one obtained from accept() on a bound server (StreamInner::Server).
Common situations: Client code that reuses a shared validation helper on its own stream; copying server-side auth checks into client code; wrapping connect() and accept() results in the same type and losing track of which end you hold.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- InvalidInput
- malformed Windows ACL for
- named-pipe client disconnected before transport…
- named-pipe client's effective token belongs to a different…
- named-pipe DACL contains a non-canonical access entry
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/3285808d4b6de208.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-core/src/local_transport/windows.rs:484
}
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 {
return Err(last_error(
"failed to impersonate the connected named-pipe client",
));
}
let guard = ImpersonationGuard { active: true };
let mut token = ptr::null_mut();
let opened = unsafe { OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, 1, &raw mut token) };
if opened == 0 || token.is_null() {
return Err(last_error(
"failed to open impersonated named-pipe client token",View on GitHub (pinned to affd8760f4)