Hmbown/CodeWhale · warning
MCP session preflight cancelled after plugin authority chang
Error message
MCP session preflight cancelled after plugin authority changed
What it means
try_establish_session races every await against a cancellation token fired when the connection's plugin authority changes (the connection is being recycled). This first bail fires when cancellation wins while resolving auth headers: the streamable session preflight is abandoned on purpose because the connection being built is already obsolete.
Source
Thrown at crates/tui/src/mcp/http.rs:191
/// line and move on — the `initialize` POST will proceed without a
/// session ID.
/// * If the server opens an SSE stream in response (the GET from old
/// SSE transport), we read only the headers, then discard the body
/// so the SSE stream is torn down. The actual SSE path uses a
/// dedicated `SseTransport` and is triggered by the incompatible-
/// status fallback in [`HttpTransport::send`].
pub(super) async fn try_establish_session(&mut self) -> Result<()> {
let cancel = self.cancel_token.clone();
let transport = match &mut self.mode {
HttpTransportMode::Streamable(t) => t,
// Already on SSE — session is implicit via the long-lived GET.
HttpTransportMode::Sse(_) => return Ok(()),
};
let headers = tokio::select! {
biased;
_ = cancel.cancelled() => {
anyhow::bail!("MCP session preflight cancelled after plugin authority changed")
}
headers = transport.auth.resolved_headers() => headers?,
};
let request = apply_safe_custom_headers(
with_default_mcp_http_headers(transport.client.get(&transport.url), false),
&headers,
);
let response = tokio::select! {
biased;
_ = cancel.cancelled() => {
anyhow::bail!("MCP session preflight cancelled after plugin authority changed")
}
response = tokio::time::timeout(Duration::from_secs(5), request.send()) => {
response
.map_err(|_| anyhow::anyhow!("GET timeout"))?
.map_err(|e| anyhow::anyhow!("GET error: {e}"))?
}
};View on GitHub (pinned to 8880682c63)
Solutions
- Retry the operation against a fresh connection - the old one is intentionally dead
- Avoid re-reviewing plugins while calls to them are in flight
- If constant, look for code that keeps churning plugin authority or dropping connections in a loop
Defensive patterns
Strategy: retry
Try / catch
match transport.try_establish_session().await {
Err(e) if e.to_string().contains("preflight cancelled after plugin authority changed") => {
// obtain a fresh connection (the old one was recycled) and retry once
}
other => other,
} Prevention
- Do not re-review or re-stage plugins while connections to them are being established
- Drop cached connections immediately after authority changes so preflights stop on stale transports
- Treat this as a transient race: a single retry on a fresh connection should succeed
When it happens
Trigger: A plugin re-review or re-stage changes its authority while a streamable HTTP connection to it is establishing; drop_connection runs concurrently with the preflight's header resolution.
Common situations: Editing or reviewing a plugin while a session to it is connecting; rapid reconnect churn during config reload.
Related errors
- MCP OAuth setup cancelled after plugin authority changed
- MCP connection '{}' was cancelled
- MCP catalog changed after tool resolution; retry the call
- active plugin registry is missing its pre-dotenv environment
- reviewed plugin MCP argument path escaped its staged root
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/c895ef78a7465f0e.
Report an issue: GitHub.