thedotmack/claude-mem · warning · ChromaUnavailableError
chroma-mcp connection in backoff
Error message
chroma-mcp connection in backoff (${Math.ceil((RECONNECT_BACKOFF_MS - timeSinceLastFailure) / 1000)}s remaining) What it means
ensureConnected refuses to (re)establish the chroma-mcp connection while a previous failed attempt is still within RECONNECT_BACKOFF_MS, throwing ChromaUnavailableError with the remaining seconds. This prevents hammering a broken chroma-mcp subprocess with reconnect attempts.
Solutions
- Wait until the reported backoff seconds elapse, then retry the operation.
- Fix the underlying chroma-mcp startup failure (check uvx availability, Chroma mode settings, logs) so the next connect succeeds.
- If shutdown or a settings change should reset the backoff, ensure dispose/reset clears lastConnectionFailureTimestamp.
- Catch ChromaUnavailableError in the caller and skip/dequeue the write until the backoff expires.
Example fix
// before
await manager.callToolUnqueued('query', args); // throws during backoff
// after
try {
await manager.callToolUnqueued('query', args);
} catch (e) {
if (e instanceof ChromaUnavailableError) queueRetryLater(e); // retry after backoff
else throw e;
} Defensive patterns
Strategy: retry
Try / catch
try {
await manager.callToolUnqueued(tool, args);
} catch (e) {
if (e instanceof ChromaUnavailableError && e.message.includes('backoff')) {
await sleep(RECONNECT_BACKOFF_MS);
retryLater(args); // requeue instead of failing the write
} else throw e;
} Prevention
- Respect the reported backoff seconds before retrying chroma operations.
- Fix root startup failures (uvx, settings, data dir) instead of retrying into the backoff window.
- Queue vector-search writes and drain them after the backoff expires.
When it happens
Trigger: Calling callToolUnqueued (or anything that reaches ensureConnected) within RECONNECT_BACKOFF_MS of the last failed connection attempt (lastConnectionFailureTimestamp > 0).
Common situations: A vector-search write/retry loop retrying faster than the backoff window after chroma-mcp crashed, failed to spawn (missing uvx), or timed out during handshake; system clock skew making timeSinceLastFailure appear small.
Related errors
- chroma-mcp transport error during
- Transport error during
- chroma-mcp call cancelled during shutdown
- chroma-mcp connection failed
- chroma-mcp subprocess closed unexpectedly, applying…
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/1283be21413780b6.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sync/ChromaMcpManager.ts:167
}
static getInstance(): ChromaMcpManager {
if (!ChromaMcpManager.instance) {
ChromaMcpManager.instance = new ChromaMcpManager();
}
return ChromaMcpManager.instance;
}
private async ensureConnected(): Promise<void> {
await this.waitForUnexpectedCloseCleanup();
if (this.connected && this.client) {
return;
}
const timeSinceLastFailure = Date.now() - this.lastConnectionFailureTimestamp;
if (this.lastConnectionFailureTimestamp > 0 && timeSinceLastFailure < RECONNECT_BACKOFF_MS) {
throw new ChromaUnavailableError(`chroma-mcp connection in backoff (${Math.ceil((RECONNECT_BACKOFF_MS - timeSinceLastFailure) / 1000)}s remaining)`);
}
if (this.connecting) {
await this.connecting;
return;
}
this.connecting = this.connectInternal();
try {
await this.connecting;
} catch (error) {
if (error instanceof ChromaMcpConnectionCancelledError) {
logger.debug('CHROMA_MCP', 'Connection attempt cancelled during shutdown');
throw error;
}
this.lastConnectionFailureTimestamp = Date.now();
if (error instanceof ChromaUnavailableError) {
// Chroma being unavailable is transient and already handled downstreamView on GitHub (pinned to d8bc9755e7)