thedotmack/claude-mem · error · ChromaUnavailableError
chroma-mcp connection failed
Error message
chroma-mcp connection failed: ${connectionError instanceof Error ? connectionError.message : String(connectionError)} What it means
When the chroma-mcp handshake (spawn, initialize, or first send) fails for any reason, connectInternal wraps the underlying error into ChromaUnavailableError with message 'chroma-mcp connection failed: <cause>' so all connect failures take the reconnect-backoff / skip-the-write path instead of surfacing a raw error (e.g. a bare 'Error: Not connected' from a mid-handshake crash).
Solutions
- Look at the wrapped cause after 'chroma-mcp connection failed:' and in logs (recordChromaVectorSearchUnavailable) for the root error.
- Run `uvx chroma-mcp --help` manually to reproduce and see the subprocess's own error output.
- Update uv/uvx and the chroma-mcp package to compatible versions.
- Inspect/repair or move aside the local persistent Chroma data directory if it is corrupted.
- Retry after the reconnect backoff; transient crashes and timeouts often recover.
Defensive patterns
Strategy: try-catch
Try / catch
try {
await manager.ensureConnected();
} catch (e) {
if (e instanceof ChromaUnavailableError && e.message.startsWith('chroma-mcp connection failed')) {
logger.warn('chroma-mcp unavailable, skipping write', { cause: e.cause ?? e.message });
queueWriteForRetry();
} else throw e;
} Prevention
- Keep uv/uvx and chroma-mcp versions compatible and updated.
- Test `uvx chroma-mcp` manually after environment or version changes.
- Watch logs for repeated handshake failures indicating a corrupted local Chroma data directory.
- Allow the reconnect backoff to work; do not bypass it with aggressive retries.
When it happens
Trigger: Calling ensureConnected/connectInternal when the uvx chroma-mcp subprocess fails to start, dies during MCP handshake, times out, or the transport send throws because the subprocess exited mid-initialization.
Common situations: Incompatible or broken uvx/Chroma versions, Python env issues under uvx, corrupted local Chroma data directory, resource exhaustion killing the subprocess, or a connection timeout on a slow machine.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- chroma-mcp subprocess closed unexpectedly, applying…
- chroma-mcp transport error during
- Connection failed, killing subprocess tree to prevent zombie
- chroma-mcp call cancelled during shutdown
- chroma-mcp connection in backoff
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/968126102212bae5.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sync/ChromaMcpManager.ts:314
}
const stderrTail = transportStderrTail();
logger.warn('CHROMA_MCP', 'Connection failed, killing subprocess tree to prevent zombie', {
error: connectionError instanceof Error ? connectionError.message : String(connectionError),
...(stderrTail ? { stderrTail } : {})
});
// Tree-kill (not just transport.close) so failed-connect descendants
// can't survive on Linux (#2313).
await this.disposeCurrentSubprocess();
// A failed MCP handshake means Chroma is unavailable, the same as a
// missing uvx, a failed prewarm, or a lost writer lock. The SDK sends
// `notifications/initialized` right after `initialize`; when the
// subprocess dies mid-handshake that send throws a bare
// `Error: Not connected`. Classify every connect failure as
// ChromaUnavailableError so callers take the reconnect-backoff and
// skip-the-write path instead of surfacing a raw error to error tracking.
const unavailableMessage = `chroma-mcp connection failed: ${connectionError instanceof Error ? connectionError.message : String(connectionError)}`;
recordChromaVectorSearchUnavailable(unavailableMessage);
throw new ChromaUnavailableError(unavailableMessage, connectionError instanceof Error ? connectionError : undefined);
}
clearTimeout(timeoutId!);
this.connected = true;
this.registerManagedProcess();
clearDependencyStatus('chroma');
logger.info('CHROMA_MCP', 'Connected to chroma-mcp successfully');
const currentTransport = this.transport;
// Captured HERE, while the child is alive and attached — not in the
// onclose handler below, which by definition runs after it has died.
const transportChild = (this.transport as unknown as { _process?: ChildProcess })._process;
const currentTracked = transportChild ? trackChild(transportChild) : null;
const currentTrackedPid = currentTracked?.pid;
this.transport.onclose = () => {
if (this.transport !== currentTransport) {
logger.debug('CHROMA_MCP', 'Ignoring stale onclose from previous transport');View on GitHub (pinned to d8bc9755e7)