{"record":{"id":"35cfcfd9ac381d04","repo":"astrid-runtime/astrid","slug":"windows-named-pipe-endpoints-are-kernel-owned-and","errorCode":null,"errorMessage":"Windows named-pipe endpoints are kernel-owned and cannot be removed while live","messagePattern":"Windows named-pipe endpoints are kernel-owned and cannot be removed while live","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/local_transport/windows.rs","lineNumber":351,"sourceCode":"        EndpointState::Available => Ok(()),\n        EndpointState::Absent => Err(io::Error::new(\n            io::ErrorKind::NotFound,\n            \"Windows named-pipe endpoint is absent\",\n        )),\n        EndpointState::BusyOrDenied => Err(io::Error::new(\n            io::ErrorKind::WouldBlock,\n            \"Windows named-pipe endpoint is occupied but unavailable\",\n        )),\n    }\n}\n\npub(super) fn endpoint_is_present(path: &Path) -> io::Result<bool> {\n    Ok(!matches!(endpoint_state(path)?, EndpointState::Absent))\n}\n\npub(super) fn remove_endpoint(path: &Path) -> io::Result<()> {\n    if endpoint_is_present(path)? {\n        return Err(io::Error::new(\n            io::ErrorKind::PermissionDenied,\n            \"Windows named-pipe endpoints are kernel-owned and cannot be removed while live\",\n        ));\n    }\n    Ok(())\n}\n\npub(super) fn remove_stale_endpoint(path: &Path) -> io::Result<bool> {\n    // Unlike a Unix socket pathname, a named-pipe endpoint cannot remain stale\n    // after its final server handle closes. Never delete or replace an object\n    // merely because its DACL makes it inaccessible.\n    let _ = endpoint_state(path)?;\n    Ok(false)\n}\n\npub(super) fn peer_is_current_user(stream: &LocalStream) -> io::Result<bool> {\n    if matches!(&stream.inner, StreamInner::Server(_)) {\n        return Ok(effective_client_user_sid(stream)?.equals(&current_user_sid()?));","sourceCodeStart":333,"sourceCodeEnd":369,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/local_transport/windows.rs#L333-L369","documentation":"On Windows, named-pipe server endpoints created via CreateNamedPipe are owned by the kernel, not the filesystem, so there is no file to unlink. remove_endpoint refuses to fake a delete and returns PermissionDenied when the endpoint still exists, succeeding only when it is already gone (e.g. after all handles were closed).","triggerScenarios":"Calling remove_endpoint(path) while a bound NamedPipeServer (or any handle to the pipe instance) is still open; endpoint_state reports the pipe as present.","commonSituations":"Cleanup code in Drop or shutdown handlers that removes the endpoint file before/without dropping the server listener; ported Unix code that unlinks a Unix domain socket path on shutdown; leaked server handles keeping the pipe alive after the app 'finished'.","solutions":["Drop (or close) the server-side stream/listener first so all pipe handles are released, then call remove_endpoint.","If you only want cleanup, make the remove best-effort: ignore PermissionDenied since the kernel deletes the pipe automatically once the last handle closes.","Restructure so remove_endpoint is called only after confirming endpoint_is_present returns false."],"exampleFix":"// before\nserver.shutdown();\ntransport.remove_endpoint(&path)?;\n// after\ndrop(server); // releases the kernel-owned pipe\nif transport.endpoint_is_present(&path)? {\n    transport.remove_endpoint(&path)?;\n}","handlingStrategy":"try-catch","validationCode":"// check before attempting removal\nif !endpoint_is_present(&path)? { /* nothing to remove; skip */ }","typeGuard":"fn can_remove(t: &Transport, p: &Path) -> bool {\n    !t.endpoint_is_present(p).unwrap_or(true)\n}","tryCatchPattern":"match transport.remove_endpoint(&path) {\n    Ok(()) => {}\n    Err(e) if e.kind() == io::ErrorKind::PermissionDenied => {\n        // pipe still kernel-owned; it disappears when the last handle drops\n        eprintln!(\"endpoint still live; skipping unlink\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always drop the server stream before cleanup/unlink logic.","Treat endpoint removal as best-effort on Windows — pipes vanish with their last handle.","Add a shutdown test asserting remove_endpoint succeeds after the listener is dropped."],"tags":["windows","named-pipes","ipc","cleanup"],"backgroundTag":"permission-denied","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}