{"record":{"id":"18338e2802a076a8","repo":"CherryHQ/cherry-studio","slug":"mcp-runtime-is-stopping","errorCode":null,"errorMessage":"MCP runtime is stopping","messagePattern":"MCP runtime is stopping","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/main/ai/mcp/McpRuntimeService.ts","lineNumber":396,"sourceCode":"  }\n\n  public async getServerLogs(serverId: string): Promise<McpServerLogEntry[]> {\n    const server = this.getServerById(serverId)\n    return this.serverLogs.get(this.getServerKey(server))\n  }\n\n  public async withClient<T>(\n    serverId: string,\n    operation: (client: Client, server: McpServer) => Promise<T>\n  ): Promise<T> {\n    const server = this.getServerById(serverId)\n    const client = await this.getOrCreateClient(server)\n    return operation(client, server)\n  }\n\n  private async getOrCreateClient(server: McpServer): Promise<Client> {\n    if (this.stopping || this.isStopped || this.isDestroyed) {\n      throw new Error('MCP runtime is stopping')\n    }\n\n    if (!server.isActive) {\n      this.setServerStatus(server.id, 'disabled')\n      throw new Error(`MCP server ${server.name} is disabled`)\n    }\n\n    const serverKey = this.getServerKey(server)\n\n    // If there's a pending initialization, wait for it\n    const pendingClient = this.pendingClients.get(serverKey)\n    if (pendingClient) {\n      this.setServerStatus(server.id, 'connecting')\n      getServerLogger(server).silly(`Waiting for pending client initialization`)\n      return pendingClient\n    }\n\n    // Check if we already have a client for this server configuration","sourceCodeStart":378,"sourceCodeEnd":414,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/mcp/McpRuntimeService.ts#L378-L414","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Catch this error in the calling layer and treat it as a non-retryable lifecycle cancellation, not a tool failure","Ensure the caller checks runtime liveness before initiating MCP operations if cancellation is common","If restarting, drain in-flight operations before calling stop/destroy","Surface a user-facing message like 'MCP server is shutting down' rather than a raw error"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Check runtime liveness before operations\nif (runtime.isStopped || runtime.isDestroyed) {\n  throw new Error('Cannot perform MCP operation: runtime is stopped')\n}","typeGuard":"function isRuntimeAlive(runtime: McpRuntimeService): boolean {\n  return !runtime.stopping && !runtime.isStopped && !runtime.isDestroyed\n}","tryCatchPattern":"try {\n  await runtime.callToolByServer({ server, name, args })\n} catch (e) {\n  if (e instanceof Error && e.message === 'MCP runtime is stopping') {\n    // Non-retryable — the app is shutting down. Abort gracefully.\n    return { cancelled: true }\n  }\n  throw e\n}","preventionTips":["Drain in-flight MCP operations before calling runtime.stop() or destroy()","Check runtime liveness flags before initiating long-running MCP operations","Distinguish lifecycle errors from operational errors in your error handling layer"],"tags":["mcp","lifecycle","shutdown","race-condition"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}