vercel/ai · error · MCPClientError
MCP SSE Transport Error: Not connected
Error message
MCP SSE Transport Error: Not connected
What it means
SseMCPTransport.send() posts JSON-RPC messages to the endpoint URL discovered during connection. If no endpoint was captured or the connection flag is false, the transport is not in a usable session state and send() throws MCPClientError immediately.
Source
Thrown at packages/mcp/src/tool/mcp-sse-transport.ts:250
});
}
async close(): Promise<void> {
this.connected = false;
this.endpoint = undefined;
this.sseConnection?.close();
this.abortController?.abort();
this.onclose?.();
}
async send(
message: JSONRPCMessage,
options?: { signal?: AbortSignal },
): Promise<void> {
options?.signal?.throwIfAborted();
if (!this.endpoint || !this.connected) {
throw new MCPClientError({
message: 'MCP SSE Transport Error: Not connected',
});
}
const endpoint = this.endpoint as URL;
const transportSignal = this.abortController?.signal;
const requestSignal =
options?.signal == null
? transportSignal
: transportSignal == null
? options.signal
: AbortSignal.any([transportSignal, options.signal]);
const attempt = async (triedAuth: boolean = false): Promise<void> => {
try {
const headers = await this.commonHeaders({
'Content-Type': 'application/json',
});View on GitHub (pinned to 69428b1f8b)
Solutions
- Recreate the client/transport (or restart the transport) and wait for connection to be established before sending
- Serialize your send calls after the connect promise resolves; don't fire requests during connection setup
- After any connection error, treat the transport as dead: build a fresh one instead of retrying send() on the same instance
Example fix
// before
transport.close();
await transport.send(message); // throws
// after
transport.close();
const transport2 = new SseMCPTransport({ url });
await client2 = createMCPClient({ transport: transport2 });
await transport2.send(message); Defensive patterns
Strategy: try-catch
Validate before calling
// expose/track connection state and check before sending
async function ensureConnected(client, makeTransport) {
if (!isConnected()) { client = await createMCPClient({ transport: makeTransport() }); }
} Type guard
function isNotConnectedError(e: unknown): boolean {
return MCPClientError.isInstance(e) && e.message.includes('Not connected');
} Try / catch
try {
await transport.send(message);
} catch (error) {
if (isNotConnectedError(error)) {
transport = await reconnect(); // recreate transport and await connection
await transport.send(message);
} else throw error;
} Prevention
- Only send after the connect promise resolves; queue messages issued during setup
- Treat any prior connection error as fatal for the transport; rebuild instead of reusing
- Check isConnected() (or equivalent state) before each send in long-running apps
When it happens
Trigger: Calling send() before start()/connect() finished establishing the SSE endpoint; calling send() after the connection dropped (e.g. after error 724) or after close(); using a transport whose establishConnection failed silently.
Common situations: Application-level retries reusing a stale transport after a disconnect; sending a request concurrently with connect() before the endpoint event arrives; forgetting that close() was called on shutdown.
Related errors
- Invalid argument for parameter requests: requests must not b
- Invalid argument for parameter requests: request IDs must no
- Invalid argument for parameter requests: request IDs must be
- Invalid argument for parameter batch: batch must be a suppor
- ACP lifecycle state data is missing.
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/cf4cf169eaf45a68.
Report an issue: GitHub.