{"record":{"id":"81fdee5f3b221aa5","repo":"thedotmack/claude-mem","slug":"chroma-mcp-call-cancelled-during-shutdown","errorCode":null,"errorMessage":"chroma-mcp call cancelled during shutdown","messagePattern":"chroma-mcp call cancelled during shutdown","errorType":"exception","errorClass":"ChromaMcpConnectionCancelledError","httpStatus":null,"severity":"info","filePath":"src/services/sync/ChromaMcpManager.ts","lineNumber":831,"sourceCode":"    await this.ensureConnected();\n\n    logger.debug('CHROMA_MCP', `Calling tool: ${toolName}`, {\n      arguments: JSON.stringify(toolArguments).slice(0, 200)\n    });\n\n    let result;\n    try {\n      result = await this.client!.callTool({\n        name: toolName,\n        arguments: toolArguments\n      });\n    } catch (transportError) {\n      logger.warn('CHROMA_MCP', `Transport error during \"${toolName}\", reconnecting and retrying once`, {\n        error: transportError instanceof Error ? transportError.message : String(transportError)\n      });\n\n      if (callGeneration !== this.connectionGeneration) {\n        throw new ChromaMcpConnectionCancelledError('chroma-mcp call cancelled during shutdown');\n      }\n\n      // Tree-kill the dying subprocess before reconnect. Previously this path\n      // just nulled the handle, which on Linux leaks the uv/python/chroma-mcp\n      // descendants every time a transport error happens (#2313).\n      await this.disposeCurrentSubprocess();\n\n      try {\n        if (callGeneration !== this.connectionGeneration) {\n          throw new ChromaMcpConnectionCancelledError('chroma-mcp call cancelled during shutdown');\n        }\n        await this.ensureConnected();\n        result = await this.client!.callTool({\n          name: toolName,\n          arguments: toolArguments\n        });\n      } catch (retryError) {\n        this.connected = false;","sourceCodeStart":813,"sourceCodeEnd":849,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/services/sync/ChromaMcpManager.ts#L813-L849","documentation":"ChromaMcpConnectionCancelledError thrown when the first chroma-mcp client.callTool() attempt fails with a transport error AND the manager's connectionGeneration has changed in the meantime, meaning a reconnect/dispose (typically triggered by shutdown) invalidated this call. Rather than retrying a stale connection, the manager cancels the in-flight call so it does not race with the new connection lifecycle.","triggerScenarios":"client.callTool() throws a transport error while, concurrently, another code path bumps connectionGeneration (disposeCurrentSubprocess/reconnect during shutdown or connection reset). The generation check after the failed attempt detects the staleness and throws.","commonSituations":"chroma-mcp subprocess crashes (uvx/python dies, OOM kill) at the same moment the user quits the app; transport drop during shutdown triggers the reconnect path but the generation token has already moved.","solutions":["Catch ChromaMcpConnectionCancelledError and treat it as a cancellation, not a data error — retry the operation after the manager is reconnected or restarted.","Check whether shutdown was expected; if the call must complete, ensure no concurrent dispose/reconnect runs while a tool call is in flight.","Add a coordination layer that stops issuing callTool requests once shutdown begins.","Update the chroma-mcp runtime (uvx/uv) if transport errors are frequent, indicating an unstable subprocess.","Enable CHROMA_MCP debug logging to confirm the generation mismatch and identify what bumped connectionGeneration."],"exampleFix":"// before\nconst result = await manager.callTool('chroma_query_documents', { query: q });\n// after\ntry {\n  const result = await manager.callTool('chroma_query_documents', { query: q });\n} catch (e) {\n  if (e instanceof ChromaMcpConnectionCancelledError) {\n    return null; // cancelled by shutdown; retry later\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isCancelled(e: unknown): e is Error {\n  return e instanceof Error && e.message.includes('call cancelled during shutdown');\n}","tryCatchPattern":"try {\n  await manager.callTool('chroma_query_documents', args);\n} catch (e) {\n  if (e instanceof ChromaMcpConnectionCancelledError) return null; // benign shutdown cancellation\n  throw e;\n}","preventionTips":["Drain in-flight tool calls before shutting down the manager","Do not call callTool concurrently with dispose/reconnect paths","Log connectionGeneration changes to diagnose mid-call invalidation","Retry the operation on a fresh manager instance if the result is required"],"tags":["chroma","mcp","shutdown","cancellation","transport"],"backgroundTag":"thread-interrupted","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}