CherryHQ/cherry-studio · warning · Error

MCP runtime is stopping

Error message

MCP runtime is stopping

What it means

Thrown by `getOrCreateClient` when the MCP runtime is in the process of shutting down. The guard checks three flags — `stopping`, `isStopped`, `isDestroyed` — and aborts client creation immediately to prevent new connections during teardown. This is a lifecycle error, not a configuration error.

Source

Thrown at src/main/ai/mcp/McpRuntimeService.ts:396

  }

  public async getServerLogs(serverId: string): Promise<McpServerLogEntry[]> {
    const server = this.getServerById(serverId)
    return this.serverLogs.get(this.getServerKey(server))
  }

  public async withClient<T>(
    serverId: string,
    operation: (client: Client, server: McpServer) => Promise<T>
  ): Promise<T> {
    const server = this.getServerById(serverId)
    const client = await this.getOrCreateClient(server)
    return operation(client, server)
  }

  private async getOrCreateClient(server: McpServer): Promise<Client> {
    if (this.stopping || this.isStopped || this.isDestroyed) {
      throw new Error('MCP runtime is stopping')
    }

    if (!server.isActive) {
      this.setServerStatus(server.id, 'disabled')
      throw new Error(`MCP server ${server.name} is disabled`)
    }

    const serverKey = this.getServerKey(server)

    // If there's a pending initialization, wait for it
    const pendingClient = this.pendingClients.get(serverKey)
    if (pendingClient) {
      this.setServerStatus(server.id, 'connecting')
      getServerLogger(server).silly(`Waiting for pending client initialization`)
      return pendingClient
    }

    // Check if we already have a client for this server configuration

View on GitHub (pinned to 726446b54c)

Solutions

  1. Catch this error in the calling layer and treat it as a non-retryable lifecycle cancellation, not a tool failure
  2. Ensure the caller checks runtime liveness before initiating MCP operations if cancellation is common
  3. If restarting, drain in-flight operations before calling stop/destroy
  4. Surface a user-facing message like 'MCP server is shutting down' rather than a raw error
Defensive patterns

Strategy: try-catch

Validate before calling

// Check runtime liveness before operations
if (runtime.isStopped || runtime.isDestroyed) {
  throw new Error('Cannot perform MCP operation: runtime is stopped')
}

Type guard

function isRuntimeAlive(runtime: McpRuntimeService): boolean {
  return !runtime.stopping && !runtime.isStopped && !runtime.isDestroyed
}

Try / catch

try {
  await runtime.callToolByServer({ server, name, args })
} catch (e) {
  if (e instanceof Error && e.message === 'MCP runtime is stopping') {
    // Non-retryable — the app is shutting down. Abort gracefully.
    return { cancelled: true }
  }
  throw e
}

Prevention

When it happens

Trigger: An in-flight operation (tool call, list tools, ping) reaches `getOrCreateClient` while the runtime is concurrently stopped (e.g. application quit, window close, or runtime restart). The race between the operation and shutdown is caught at the client-acquisition boundary.

Common situations: Application is closing and a pending AI request triggers a tool call; the user disables all MCP servers during an active session; a runtime restart races with concurrent tool invocations.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/18338e2802a076a8. Report an issue: GitHub.