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

  1. Inspect the retry error message (the underlying retryError.message) and the earlier transport warning log for the root cause.
  2. Check system memory and dmesg/OOM-killer logs; if chroma-mcp is being killed, free memory or raise limits.
  3. Confirm the Chroma data directory is not corrupted; if so, reindex/backfill into a fresh directory.
  4. 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.
  5. 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

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


AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12). Data as JSON: /api/errors/94f8e5b541e6bbfa. Report an issue: GitHub.