astrid-runtime/astrid · error
named-pipe endpoint path must not contain a parent component
Error message
named-pipe endpoint path must not contain a parent component
What it means
pipe_name converts a user-supplied endpoint path into a per-user kernel pipe name (\\.\pipe\...). A `..` component would let the resulting pipe name escape the intended per-user namespace, so the library rejects any path containing a ParentDir component with InvalidInput before absolutizing it.
Source
Thrown at crates/astrid-core/src/local_transport/windows.rs:576
options
.first_pipe_instance(first)
.reject_remote_clients(true);
// SAFETY: `security.attributes` and its LocalAlloc-owned descriptor remain
// valid for the complete CreateNamedPipeW call. Tokio does not retain the
// pointer after `create_with_security_attributes_raw` returns.
unsafe {
options
.create_with_security_attributes_raw(pipe_name, (&raw mut security.attributes).cast())
}
}
fn pipe_name(path: &Path) -> io::Result<OsString> {
if path
.components()
.any(|component| matches!(component, std::path::Component::ParentDir))
{
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"named-pipe endpoint path must not contain a parent component",
));
}
let absolute = std::path::absolute(path)?;
let sid = current_user_sid()?;
let endpoint = blake3::hash(absolute.as_os_str().as_encoded_bytes());
let digest = blake3::hash(sid.as_bytes());
Ok(OsString::from(format!(
"{PIPE_PREFIX}{}{}",
&digest.to_hex()[..24],
&endpoint.to_hex()[..40]
)))
}
#[cfg(feature = "test-support")]
pub(super) fn endpoint_name_for_test(path: &Path) -> io::Result<OsString> {
pipe_name(path)View on GitHub (pinned to affd8760f4)
Solutions
- Normalize the path before passing it: use std::path::absolute() (or Path::components-based cleanup) so no ParentDir component remains.
- Build endpoint paths from a fixed base with PathBuf::join on simple names only.
- Reject or canonicalize user/config-supplied endpoint paths at config-load time.
Example fix
// before
let ep = Path::new(&format!("{}\\..\\svc", base));
transport.bind(ep)?;
// after
let ep = std::path::absolute(Path::new(&base))?.join("svc");
transport.bind(&ep)?; Defensive patterns
Strategy: validation
Validate before calling
fn endpoint_path_ok(p: &Path) -> bool {
!p.components().any(|c| matches!(c, std::path::Component::ParentDir))
}
// call before bind/connect:
assert!(endpoint_path_ok(&cfg.endpoint)); Type guard
fn safe_endpoint(p: &Path) -> Option<PathBuf> {
if p.components().any(|c| matches!(c, std::path::Component::ParentDir)) {
None
} else {
std::path::absolute(p).ok()
}
} Try / catch
match transport.bind(&path) {
Err(e) if e.kind() == io::ErrorKind::InvalidInput
&& e.to_string().contains("parent component") => {
eprintln!("endpoint {:?} contains '..'; fix config", path);
}
other => other?,
} Prevention
- Normalize endpoint paths with std::path::absolute before use.
- Never interpolate raw user input into endpoint paths.
- Validate endpoint paths once at config load, not at connect time.
When it happens
Trigger: Passing a path with `..` segments to connect(), bind(), or the test helpers — e.g. bind("/tmp/astrid/../service.sock") or a config value assembled by string concatenation with `..`.
Common situations: Endpoint paths built by joining user input or env vars without normalization; configs ported from Unix socket setups with relative paths; typos like "..\pipe" in settings files.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- named-pipe peer process changed during authentication
- named-pipe peer PID did not identify the opened process
- named-pipe peer process belongs to a different operating-sys
- named-pipe client's effective token belongs to a different o
- Windows named-pipe endpoint denied access
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/edd23cf1d05f8452.
Report an issue: GitHub.