{"record":{"id":"3523a8927ad3506c","repo":"astrid-runtime/astrid","slug":"mcp-attach-replaced-or-idle-evicted","errorCode":null,"errorMessage":"MCP attach replaced or idle-evicted","messagePattern":"MCP attach replaced or idle-evicted","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/astrid-cli/src/commands/mcp/gateway.rs","lineNumber":846,"sourceCode":") -> Result<()>\nwhere\n    S: rmcp::Service<RoleServer>,\n    R: tokio::io::AsyncRead + Unpin + Send + 'static,\n{\n    let running = server\n        .serve_with_ct((reader, write_half), cancel.clone())\n        .await\n        .context(\"MCP gateway failed to initialize attach session\")?;\n    // Use the slot generation rather than the host session id so a delayed\n    // predecessor cannot remove a replacement's peer from the shared watcher.\n    let peer_key = slot_id.to_string();\n    peers\n        .lock()\n        .await\n        .insert(peer_key.clone(), running.peer().clone());\n    let result = tokio::select! {\n        result = running.waiting() => result.context(\"MCP gateway attach transport terminated abnormally\"),\n        () = cancel.cancelled() => Err(anyhow::anyhow!(\"MCP attach replaced or idle-evicted\")),\n    };\n    peers.lock().await.remove(&peer_key);\n    result.map(|_| ())\n}\n\nasync fn read_registration<R>(reader: &mut BufReader<R>) -> Result<AttachRegistration>\nwhere\n    R: AsyncRead + Unpin,\n{\n    timeout(REGISTRATION_TIMEOUT, read_registration_inner(reader))\n        .await\n        .context(\"timed out reading MCP attach registration\")?\n}\n\nasync fn read_registration_inner<R>(reader: &mut BufReader<R>) -> Result<AttachRegistration>\nwhere\n    R: AsyncRead + Unpin,\n{","sourceCodeStart":828,"sourceCodeEnd":864,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/mcp/gateway.rs#L828-L864","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-run the attach; if this session was intentionally replaced, the new session is already active and nothing else is needed.","Avoid concurrent attaches with the same principal/peer key; detach the old session first.","Keep the session active (or raise the idle-eviction policy) if idle timeouts are evicting it.","Check gateway logs for who issued the cancellation (replacement vs. eviction) to confirm the cause."],"exampleFix":"// before\nlet result = run_attached_session(...).await?; // hard-fails on replace/evict\n// after\nif let Err(e) = run_attached_session(...).await {\n    if e.to_string().contains(\"replaced or idle-evicted\") {\n        eprintln!(\"session superseded; reconnecting...\");\n        run_attached_session(...).await?;\n    } else { return Err(e); }\n}","handlingStrategy":"retry","validationCode":"// Detect a live session with the same peer key before attaching\nif gateway_has_active_session(&peer_key) {\n    eprintln!(\"session already attached; it will be replaced\");\n}","typeGuard":null,"tryCatchPattern":"match run_attached_session(...).await {\n    Err(e) if e.to_string().contains(\"replaced or idle-evicted\") => {\n        eprintln!(\"superseded; reconnecting\");\n        run_attached_session(...).await?;\n    }\n    other => other?,\n}","preventionTips":["Do not attach the same principal/project from two clients simultaneously","Keep sessions active or tune the idle-eviction policy","Treat this error as an expected lifecycle event, not a crash","Implement automatic reconnect with backoff in long-lived clients"],"tags":["mcp","gateway","session","cancellation"],"backgroundTag":"invalid-state-transition","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}