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

  1. Wait until the reported backoff seconds elapse, then retry the operation.
  2. Fix the underlying chroma-mcp startup failure (check uvx availability, Chroma mode settings, logs) so the next connect succeeds.
  3. If shutdown or a settings change should reset the backoff, ensure dispose/reset clears lastConnectionFailureTimestamp.
  4. 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

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


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 downstream

View on GitHub (pinned to d8bc9755e7)