Hmbown/CodeWhale · error · anyhow::Error

MCP session expired: {error}

Error message

MCP session expired: {error}

What it means

After matching the expected JSON-RPC id, recv inspects the response's error member with is_mcp_stale_session_body; if the server reports an expired/unknown session (typical of MCP streamable-HTTP servers whose session id has aged out or been forgotten), this error is raised instead of returning the raw error. It signals that the MCP session established at initialize time is no longer valid on the server side, while the transport itself may still be alive.

Source

Thrown at crates/tui/src/mcp.rs:2234

                    };
                    return Err(err).with_context(|| {
                        format!(
                            "Invalid MCP JSON-RPC message from server '{}': {}",
                            self.name, preview
                        )
                    });
                }
            };

            // Check if this is a response with the expected id. We emit
            // string IDs because some MCP gateways reject numeric JSON-RPC
            // IDs, but accept numeric echoes for compatibility with older
            // servers and tests.
            if response_id_matches(value.get("id"), &expected_id) {
                if let Some(error) = value.get("error")
                    && is_mcp_stale_session_body(&error.to_string())
                {
                    anyhow::bail!("MCP session expired: {error}");
                }
                return Ok(value);
            }
            // Skip notifications (no id) and responses with different ids
        }
    }

    /// Gracefully close the connection
    #[allow(dead_code)] // Public API for MCP consumers
    pub fn close(&mut self) {
        self.cancel_token.cancel();
        self.state = ConnectionState::Disconnected;
    }

    fn catalog_authorized(&self) -> bool {
        self.config
            .reviewed_plugin
            .as_ref()

View on GitHub (pinned to 8880682c63)

Solutions

  1. Reconnect: drop the connection and call get_or_connect again — a fresh initialize handshake obtains a new session id.
  2. If it recurs frequently, check the server/gateway's session TTL and align it with expected idle periods, or enable client-side periodic activity.
  3. Verify nothing between client and server (proxy, LB) strips the session header on responses.
  4. If the server is supposed to be stateless, check its MCP implementation version — a bug may invalidate sessions prematurely.

Example fix

// before
let result = pool.get_or_connect("remote").await?.call_tool(...).await;
// Err: MCP session expired: {...}

// after — drop and re-establish the session
if let Err(e) = pool.get_or_connect("remote").await?.call_tool(...).await {
    if e.to_string().contains("MCP session expired") {
        pool.drop_connection("remote", "stale session");
        return pool.get_or_connect("remote").await?.call_tool(...).await;
    }
    return Err(e);
}
Defensive patterns

Strategy: retry

Try / catch

// Rust: stale MCP session -> drop connection, re-initialize, retry once
let r = pool.get_or_connect(server).await?.call_tool(n, a, t).await;
match r {
    Err(e) if e.to_string().contains("MCP session expired") => {
        pool.drop_connection(server, "stale session");
        pool.get_or_connect(server).await?.call_tool(n, a, t).await
    }
    o => o,
}

Prevention

When it happens

Trigger: A streamable-HTTP MCP server issues a session id at initialize, then expires or restarts its session store; the next request on the old session returns a JSON-RPC error whose body matches the stale-session detector, and recv converts it to this message.

Common situations: Long-lived TUI sessions against an MCP gateway behind a load balancer that rotates sessions; server redeploy mid-session; gateway idle-timeout shorter than the client's connection lifetime; resuming a laptop from sleep with the HTTP session long expired server-side.

Related errors


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