{"record":{"id":"94f8e5b541e6bbfa","repo":"thedotmack/claude-mem","slug":"chroma-mcp-transport-error-during-toolname-r","errorCode":null,"errorMessage":"chroma-mcp transport error during \"${toolName}\" (retry failed): ${retryError instanceof Error ? retryError.message : String(retryError)}","messagePattern":"chroma-mcp transport error during \"(.+?)\" \\(retry failed\\): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/services/sync/ChromaMcpManager.ts","lineNumber":754,"sourceCode":"      }\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;\n        throw new Error(`chroma-mcp transport error during \"${toolName}\" (retry failed): ${retryError instanceof Error ? retryError.message : String(retryError)}`);\n      }\n    }\n\n    if (result.isError) {\n      const errorText = (result.content as Array<{ type: string; text?: string }>)\n        ?.find(item => item.type === 'text')?.text || 'Unknown chroma-mcp error';\n      throw new Error(`chroma-mcp tool \"${toolName}\" returned error: ${errorText}`);\n    }\n\n    const contentArray = result.content as Array<{ type: string; text?: string }>;\n    if (!contentArray || contentArray.length === 0) {\n      return null;\n    }\n\n    const firstTextContent = contentArray.find(item => item.type === 'text' && item.text);\n    if (!firstTextContent || !firstTextContent.text) {\n      return null;\n    }","sourceCodeStart":736,"sourceCodeEnd":772,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d768ba364302d12b76e69e4f021f0bb1d2d50ed6/src/services/sync/ChromaMcpManager.ts#L736-L772","documentation":"Thrown by callToolUnqueued after a MCP transport failure: the first client.callTool threw, the manager disposed the dying subprocess, reconnected, retried the call exactly once, and the retry also threw. It is a plain Error (NOT a ChromaUnavailableError), which is why downstream callers like ChromaSync.queryChroma rely on substring matching of the message text. this.connected is set to false so the next call will re-establish the transport.","triggerScenarios":"MCP stdio/transport to the chroma-mcp subprocess breaks (subprocess crashed, pipe closed, EOF) and the single retry after disposeCurrentSubprocess + ensureConnected still fails; or a shutdown raced in and ensureConnected could not re-establish.","commonSituations":"The chroma-mcp subprocess is OOM-killed or crashes mid-call; the system is under memory pressure so the respawned subprocess dies again; the data dir grew large enough that reconnect+retry exceeds implicit limits; an antivirus killing the python child repeatedly.","solutions":["Inspect the retry error message (the underlying retryError.message) and the earlier transport warning log for the root cause.","Check system memory and dmesg/OOM-killer logs; if chroma-mcp is being killed, free memory or raise limits.","Confirm the Chroma data directory is not corrupted; if so, reindex/backfill into a fresh directory.","Let the manager self-heal: this.connected is now false, so a subsequent call triggers ensureConnected again — a retry at the caller level after backoff may succeed.","If it recurs, bump uvx/chroma-mcp logging or run `uvx chroma-mcp` standalone to reproduce the subprocess crash."],"exampleFix":"// before: caller lets the transport error propagate unrecoverably\nconst r = await manager.callTool('chroma_query_documents', args);\n// after: caller treats transport errors as transient and backs off\ntry {\n  const r = await manager.callTool('chroma_query_documents', args);\n} catch (e) {\n  if (/transport error.*retry failed/.test(e.message)) { await sleep(1000); return retry(); }\n  throw e;\n}","handlingStrategy":"retry","validationCode":"// Health gate before issuing the real call\nif (!(await manager.isHealthy())) { /* skip or re-prewarm */ }","typeGuard":"function isTransportRetryFailure(e: unknown): boolean {\n  return e instanceof Error && /chroma-mcp transport error.*retry failed/i.test(e.message);\n}","tryCatchPattern":"try { return await manager.callTool(toolName, args); }\ncatch (e) {\n  if (isTransportRetryFailure(e)) { await sleep(1000); return await manager.callTool(toolName, args); }\n  throw e;\n}","preventionTips":["Monitor memory; an OOM-killed chroma-mcp child is the most common repeat transport failure.","Keep Chroma data dir on fast local storage so reconnect+retry completes quickly.","Let this.connected=false self-heal the next call rather than crashing the worker.","Run `uvx chroma-mcp` standalone to reproduce and fix recurring subprocess crashes."],"tags":["chroma","mcp","transport","subprocess","retry","vector-search"],"backgroundTag":null,"analyzedSha":"d768ba364302d12b76e69e4f021f0bb1d2d50ed6","analyzedAt":"2026-08-12T23:52:55.241Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}