astrid-runtime/astrid · warning

MCP attach replaced or idle-evicted

Error message

MCP attach replaced or idle-evicted

What it means

In `run_attached_session`, the attached session normally ends when the transport terminates (`running.waiting()`), but if the cancellation token fires first, this error is thrown. Cancellation means the session was replaced by a newer attach (same principal/peer key) or idle-evicted by the gateway, so the session was deliberately terminated rather than failing naturally.

Solutions

  1. Re-run the attach; if this session was intentionally replaced, the new session is already active and nothing else is needed.
  2. Avoid concurrent attaches with the same principal/peer key; detach the old session first.
  3. Keep the session active (or raise the idle-eviction policy) if idle timeouts are evicting it.
  4. Check gateway logs for who issued the cancellation (replacement vs. eviction) to confirm the cause.

Example fix

// before
let result = run_attached_session(...).await?; // hard-fails on replace/evict
// after
if let Err(e) = run_attached_session(...).await {
    if e.to_string().contains("replaced or idle-evicted") {
        eprintln!("session superseded; reconnecting...");
        run_attached_session(...).await?;
    } else { return Err(e); }
}
Defensive patterns

Strategy: retry

Validate before calling

// Detect a live session with the same peer key before attaching
if gateway_has_active_session(&peer_key) {
    eprintln!("session already attached; it will be replaced");
}

Try / catch

match run_attached_session(...).await {
    Err(e) if e.to_string().contains("replaced or idle-evicted") => {
        eprintln!("superseded; reconnecting");
        run_attached_session(...).await?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: A second `mcp attach` with the same peer key replaces the first session; the gateway's idle eviction policy cancels an inactive session; the gateway explicitly cancels the session's token during shutdown or lifecycle transitions.

Common situations: Opening the same MCP project in two editors/terminals so the newer attach supersedes the older one; leaving a session idle past the eviction timeout; the gateway restarting or being stopped while a session is attached.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at crates/astrid-cli/src/commands/mcp/gateway.rs:846

) -> Result<()>
where
    S: rmcp::Service<RoleServer>,
    R: tokio::io::AsyncRead + Unpin + Send + 'static,
{
    let running = server
        .serve_with_ct((reader, write_half), cancel.clone())
        .await
        .context("MCP gateway failed to initialize attach session")?;
    // Use the slot generation rather than the host session id so a delayed
    // predecessor cannot remove a replacement's peer from the shared watcher.
    let peer_key = slot_id.to_string();
    peers
        .lock()
        .await
        .insert(peer_key.clone(), running.peer().clone());
    let result = tokio::select! {
        result = running.waiting() => result.context("MCP gateway attach transport terminated abnormally"),
        () = cancel.cancelled() => Err(anyhow::anyhow!("MCP attach replaced or idle-evicted")),
    };
    peers.lock().await.remove(&peer_key);
    result.map(|_| ())
}

async fn read_registration<R>(reader: &mut BufReader<R>) -> Result<AttachRegistration>
where
    R: AsyncRead + Unpin,
{
    timeout(REGISTRATION_TIMEOUT, read_registration_inner(reader))
        .await
        .context("timed out reading MCP attach registration")?
}

async fn read_registration_inner<R>(reader: &mut BufReader<R>) -> Result<AttachRegistration>
where
    R: AsyncRead + Unpin,
{

View on GitHub (pinned to affd8760f4)