thedotmack/claude-mem · error · Error
chroma-mcp transport error during "${toolName}" (retry faile
Error message
chroma-mcp transport error during "${toolName}" (retry failed): ${retryError instanceof Error ? retryError.message : String(retryError)} What it means
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.
Source
Thrown at src/services/sync/ChromaMcpManager.ts:754
}
// Tree-kill the dying subprocess before reconnect. Previously this path
// just nulled the handle, which on Linux leaks the uv/python/chroma-mcp
// descendants every time a transport error happens (#2313).
await this.disposeCurrentSubprocess();
try {
if (callGeneration !== this.connectionGeneration) {
throw new ChromaMcpConnectionCancelledError('chroma-mcp call cancelled during shutdown');
}
await this.ensureConnected();
result = await this.client!.callTool({
name: toolName,
arguments: toolArguments
});
} catch (retryError) {
this.connected = false;
throw new Error(`chroma-mcp transport error during "${toolName}" (retry failed): ${retryError instanceof Error ? retryError.message : String(retryError)}`);
}
}
if (result.isError) {
const errorText = (result.content as Array<{ type: string; text?: string }>)
?.find(item => item.type === 'text')?.text || 'Unknown chroma-mcp error';
throw new Error(`chroma-mcp tool "${toolName}" returned error: ${errorText}`);
}
const contentArray = result.content as Array<{ type: string; text?: string }>;
if (!contentArray || contentArray.length === 0) {
return null;
}
const firstTextContent = contentArray.find(item => item.type === 'text' && item.text);
if (!firstTextContent || !firstTextContent.text) {
return null;
}View on GitHub (pinned to d768ba3643)
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.
Example fix
// before: caller lets the transport error propagate unrecoverably
const r = await manager.callTool('chroma_query_documents', args);
// after: caller treats transport errors as transient and backs off
try {
const r = await manager.callTool('chroma_query_documents', args);
} catch (e) {
if (/transport error.*retry failed/.test(e.message)) { await sleep(1000); return retry(); }
throw e;
} Defensive patterns
Strategy: retry
Validate before calling
// Health gate before issuing the real call
if (!(await manager.isHealthy())) { /* skip or re-prewarm */ } Type guard
function isTransportRetryFailure(e: unknown): boolean {
return e instanceof Error && /chroma-mcp transport error.*retry failed/i.test(e.message);
} Try / catch
try { return await manager.callTool(toolName, args); }
catch (e) {
if (isTransportRetryFailure(e)) { await sleep(1000); return await manager.callTool(toolName, args); }
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- chroma-mcp prewarm failed: ${errorMessage}
- chroma-mcp tool "${toolName}" returned error: ${errorText}
- Worker API error (${response.status}): ${errorText}
- chroma-mcp connection in backoff (${Math.ceil((RECONNECT_BAC
- uvx executable not found for chroma-mcp (${uvxSpawnCommand})
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/94f8e5b541e6bbfa.
Report an issue: GitHub.