{"record":{"id":"fbd273670c7c015f","repo":"astrid-runtime/astrid","slug":"mcp-gateway-principal-channel-was-not-initialized","errorCode":null,"errorMessage":"MCP gateway principal channel was not initialized","messagePattern":"MCP gateway principal channel was not initialized","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/mcp/gateway.rs","lineNumber":197,"sourceCode":"            |connected| {\n                super::require_authenticated_unless_anonymous(\n                    &principal,\n                    connected.is_authenticated(),\n                )\n            },\n        )\n        .await\n        .context(\"failed to recover the persistent daemon uplink\")?;\n        Ok(())\n    }\n\n    async fn peers_for(&self, principal: &str) -> Result<Peers> {\n        self.peers\n            .lock()\n            .await\n            .get(principal)\n            .cloned()\n            .ok_or_else(|| anyhow::anyhow!(\"MCP gateway principal channel was not initialized\"))\n    }\n\n    async fn semaphore_for(&self, principal: &str) -> Arc<Semaphore> {\n        self.permits\n            .lock()\n            .await\n            .entry(principal.to_owned())\n            .or_insert_with(|| Arc::new(Semaphore::new(MAX_ATTACHES)))\n            .clone()\n    }\n\n    async fn acquire(&self, principal: &str) -> Result<OwnedSemaphorePermit> {\n        let semaphore = self.semaphore_for(principal).await;\n        if let Ok(permit) = semaphore.clone().try_acquire_owned() {\n            return Ok(permit);\n        }\n        if self.evict_lru_idle().await\n            && let Ok(permit) = semaphore.try_acquire_owned()","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/mcp/gateway.rs#L179-L215","documentation":"The MCP gateway keeps a per-principal map of active peer channels (`peers`). `peers_for` looks up the channel for a requested principal and throws this error when no entry exists, meaning the attach/session handler asked for a principal whose channel was never registered (or was already removed). It is an internal consistency check: callers are expected to have registered the peer before resolving it.","triggerScenarios":"Calling gateway APIs that resolve a peer by principal (e.g. message routing during attach) when the principal's channel was never inserted into the `peers` map, or after it was removed (peer disconnected, idle-evicted, or replaced by a new attach as in `run_attached_session`).","commonSituations":"A concurrent attach replaced the peer entry and evicted the old session; the gateway restarted between registration and use; a race between peer removal and an in-flight request referencing the principal; passing a wrong/misspelled principal identifier to an internal routing path.","solutions":["Check for concurrent `mcp attach` sessions using the same principal; reconnect the evicted session.","Verify the principal identifier used is the exact one registered during attach.","Restart the gateway so peer registration state is rebuilt cleanly.","If reproducible, fix the code path to register the peer before resolving it (move `peers.insert` earlier or keep a strong reference for the session's lifetime)."],"exampleFix":"// before\nlet peer = gateway.peers_for(&principal).await?;\n// after\nlet peer = match gateway.peers_for(&principal).await {\n    Ok(p) => p,\n    Err(_) => return Err(anyhow::anyhow!(\"peer {principal} no longer attached; reconnect the MCP session\")),\n};","handlingStrategy":"try-catch","validationCode":"// Go-style pre-check is not exposed; guard at call site\nif gateway.peer_keys().await.contains(&principal) { /* proceed */ }","typeGuard":"fn is_peer_registered(map: &PeersMap, principal: &str) -> bool { map.contains_key(principal) }","tryCatchPattern":"match gateway.peers_for(&principal).await {\n    Ok(peer) => use(peer),\n    Err(e) if e.to_string().contains(\"not initialized\") => reconnect_session(),\n    Err(e) => return Err(e),\n}","preventionTips":["Register the peer in the map before any code path can resolve it","Keep the peer handle alive for the full session lifetime","Log peer insert/remove events to diagnose eviction races","Reconnect promptly when a session is replaced or evicted"],"tags":["mcp","gateway","state","concurrency"],"backgroundTag":"internal-invariant-violation","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"}