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

  1. Retry the operation against a fresh connection - the old one is intentionally dead
  2. Avoid re-reviewing plugins while calls to them are in flight
  3. 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

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


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/c895ef78a7465f0e. Report an issue: GitHub.