vercel/ai · warning · MCPClientError

MCP client initialization was aborted

Error message

MCP client initialization was aborted

What it means

Thrown in the init catch path when the external AbortSignal passed to createMCPClient was aborted during initialization. The client re-wraps the abort as MCPClientError with the signal's reason as cause, after the transport is closed.

Source

Thrown at packages/mcp/src/tool/mcp-client.ts:595

        {
          method: 'notifications/initialized',
        },
        { signal },
      );

      return this;
    } catch (error) {
      try {
        await waitForAbort(this.transport.close({ signal }), signal);
      } catch {}
      this.onClose();

      if (timeoutError != null) {
        throw timeoutError;
      }

      if (externalSignal?.aborted) {
        throw new MCPClientError({
          message: 'MCP client initialization was aborted',
          cause: externalSignal.reason,
        });
      }

      throw error;
    } finally {
      if (timeoutId != null) {
        clearTimeout(timeoutId);
      }
    }
  }

  private async tryProtocolDiscovery(
    signal: AbortSignal | undefined,
  ): Promise<boolean> {
    this.protocolEra = 'modern';
    this.protocolVersion = LATEST_PROTOCOL_VERSION;

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Catch MCPClientError and treat message 'MCP client initialization was aborted' as an expected cancellation, not a server failure.
  2. Increase the timeout if the abort is from your own deadline and the server is slow to initialize.
  3. Check error.cause (the signal reason) to identify who aborted.
  4. Avoid aborting the signal during setup, or retry creation after the abort reason is resolved.

Example fix

// before
const client = await createMCPClient({ transport, signal: controller.signal });
// after
try {
  const client = await createMCPClient({ transport, signal: controller.signal });
} catch (e) {
  if (MCPClientError.isInstance(e) && e.message.includes('aborted')) return; // expected cancellation
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (controller.signal.aborted) {
  return; // do not start client creation with an already-aborted signal
}

Type guard

function isAbortError(e: unknown): boolean {
  return e instanceof Error && (e.name === 'AbortError' || e.message.includes('aborted'));
}

Try / catch

try {
  const client = await createMCPClient({ transport, signal: controller.signal });
} catch (e) {
  if (MCPClientError.isInstance(e) && e.message === 'MCP client initialization was aborted') {
    return; // expected cancellation, use e.cause to see why
  }
  throw e;
}

Prevention

When it happens

Trigger: Caller-supplied AbortSignal fires while the initialize handshake/discovery request is in flight; e.g. a request timeout, user navigation, or explicit abortController.abort() during client creation.

Common situations: React effect cleanups aborting in-flight client creation; server taking longer than a caller-imposed timeout; user navigating away; request cancellations in serverless functions.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/6bcd4d42337c9ff4. Report an issue: GitHub.