astrid-runtime/astrid · error

Windows named-pipe endpoint is occupied but unavailable

Error message

Windows named-pipe endpoint is occupied but unavailable

What it means

probe() reports EndpointState::BusyOrDenied as io::ErrorKind::WouldBlock with this message: the named pipe exists, but it is occupied (all instances busy, ERROR_PIPE_BUSY) or access is denied, so a connection attempt cannot proceed right now. Unlike the Absent case, retrying later may succeed once the server frees a pipe instance.

Source

Thrown at crates/astrid-core/src/local_transport/windows.rs:338

}

pub(super) fn split(
    stream: LocalStream,
) -> (
    tokio::io::ReadHalf<LocalStream>,
    tokio::io::WriteHalf<LocalStream>,
) {
    tokio::io::split(stream)
}

pub(super) fn probe(path: &Path) -> io::Result<()> {
    match endpoint_state(path)? {
        EndpointState::Available => Ok(()),
        EndpointState::Absent => Err(io::Error::new(
            io::ErrorKind::NotFound,
            "Windows named-pipe endpoint is absent",
        )),
        EndpointState::BusyOrDenied => Err(io::Error::new(
            io::ErrorKind::WouldBlock,
            "Windows named-pipe endpoint is occupied but unavailable",
        )),
    }
}

pub(super) fn endpoint_is_present(path: &Path) -> io::Result<bool> {
    Ok(!matches!(endpoint_state(path)?, EndpointState::Absent))
}

pub(super) fn remove_endpoint(path: &Path) -> io::Result<()> {
    if endpoint_is_present(path)? {
        return Err(io::Error::new(
            io::ErrorKind::PermissionDenied,
            "Windows named-pipe endpoints are kernel-owned and cannot be removed while live",
        ));
    }
    Ok(())

View on GitHub (pinned to affd8760f4)

Solutions

  1. Retry with backoff, or use the client's open_with_retry path which waits for pipe availability on ERROR_PIPE_BUSY
  2. Increase the server's named-pipe instance count when creating the pipe (nMaxInstances) so concurrent clients can connect
  3. If it is denial rather than busy, align the client's account with the pipe ACL (see PermissionDenied handling)

Example fix

// before
probe(&pipe_path)?; // WouldBlock when busy
// after
loop {
    match probe(&pipe_path) {
        Ok(()) => break,
        Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
            tokio::time::sleep(Duration::from_millis(50)).await;
        },
        Err(e) => return Err(e),
    }
}
Defensive patterns

Strategy: retry

Try / catch

match probe(&pipe) {
    Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
        // busy or denied: back off and retry, or use open_with_retry
    },
    other => other?,
}

Prevention

When it happens

Trigger: Calling probe(path) when the pipe's instance count is exhausted (all instances connected) or the pipe ACL denies the probing client (ERROR_ACCESS_DENIED), i.e. endpoint_state returns BusyOrDenied.

Common situations: More clients than the server's pipe instance limit (default CreateNamedPipe instance count of 1) are trying to connect concurrently; long-lived connections exhaust the server's instances; ACL mismatch between client and server accounts.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/9c34a43e4782c6a2. Report an issue: GitHub.