astrid-runtime/astrid · error
MCP gateway attach limit reached for principal '{principal}'
Error message
MCP gateway attach limit reached for principal '{principal}' ({MAX_ATTACHES}) What it means
The MCP gateway caps each principal at MAX_ATTACHES = 16 live attach sessions. In `acquire`, after trying a plain semaphore permit and then evicting the least-recently-used idle slot, if no permit can be obtained the gateway rejects the attach. This is an intentional process-local resource cap so a hostile host cannot make one principal consume an unbounded number of broker sessions.
Source
Thrown at crates/astrid-cli/src/commands/mcp/gateway.rs:219
self.permits
.lock()
.await
.entry(principal.to_owned())
.or_insert_with(|| Arc::new(Semaphore::new(MAX_ATTACHES)))
.clone()
}
async fn acquire(&self, principal: &str) -> Result<OwnedSemaphorePermit> {
let semaphore = self.semaphore_for(principal).await;
if let Ok(permit) = semaphore.clone().try_acquire_owned() {
return Ok(permit);
}
if self.evict_lru_idle().await
&& let Ok(permit) = semaphore.try_acquire_owned()
{
return Ok(permit);
}
anyhow::bail!(
"MCP gateway attach limit reached for principal '{principal}' ({MAX_ATTACHES})"
)
}
/// Reserve a host session through replacement, cap admission, and slot
/// installation as one linearizable operation. The reservation owns the
/// admission guard until `install` publishes the new slot.
async fn reserve_session(
&self,
host_session_id: &str,
principal: &str,
) -> Result<AttachReservation> {
let admission = Arc::clone(&self.admission).lock_owned().await;
if self.shutdown.is_cancelled() {
anyhow::bail!("MCP gateway is shutting down");
}
self.replace_session(host_session_id).await?;
let permit = self.acquire(principal).await?;View on GitHub (pinned to affd8760f4)
Solutions
- Reduce the number of concurrent `mcp attach` sessions for this principal to at most 16; close or disconnect idle/stale attach processes.
- Check for orphaned gateway attach processes (ps / lsof on the Unix socket) and kill stale ones so their slots free up.
- Retry the attach after existing sessions go idle or EOF; idle slots are evicted LRU automatically.
- If 16 is genuinely too small for your workload, this cap is a compile-time constant (MAX_ATTACHES) and requires a code change, not configuration.
Example fix
// before: many parallel attach clients each opening their own session
for i in 0..32 { spawn(mcp_attach(principal)); }
// after: reuse one attach session per host-session id, or close idle ones
for i in 0..32 { spawn(mcp_attach(principal)).join().unwrap(); } // or pool <= 16 sessions Defensive patterns
Strategy: validation
Validate before calling
fn can_attach(live_sessions: usize, all_active: bool) -> bool {
live_sessions < 16 || !all_active // a slot must be free or LRU-evictable
} Try / catch
match gateway.reserve_session(host_session_id, principal).await {
Err(e) if e.to_string().contains("attach limit reached") => queue_attach_for_retry(),
other => other?,
} Prevention
- Pool attach sessions; one per host-session id, capped well below 16.
- Tear down attach sessions on editor/agent exit so slots are released.
- Detect orphaned attach processes before starting new ones.
- Retry with backoff on this error instead of spawning more sessions.
When it happens
Trigger: Calling reserve_session (via acquire) when the principal already holds 16 live attach slots and none of them are idle enough to be LRU-evicted; also hit when a replacement attach cannot be admitted within REPLACEMENT_TIMEOUT because the predecessor has not released its slot.
Common situations: Opening more than 16 concurrent MCP attach sessions for the same principal (e.g. many editor windows or agent processes each running `mcp attach`); leaked/stale sessions that are still considered active so LRU eviction cannot free a slot; reconnect storms during flaky network conditions.
Related errors
- MCP gateway principal channel was not initialized
- MCP gateway is shutting down
- {primary:#}; additional gateway cleanup failure: {secondary:
- expected MCP attach registration
- MCP attach registration is missing or too large
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/aa468c9e86814bb4.
Report an issue: GitHub.