thedotmack/claude-mem · warning · ChromaUnavailableError

Chroma unavailable before write; leaving documents unsynced

Error message

Chroma unavailable before write; leaving documents unsynced

What it means

addDocuments() calls ensureCollectionExists() before writing; if that fails with ChromaUnavailableError, the batch is intentionally left unsynced: the method returns 0 and the documents stay in SQLite for a later backfill. Non-availability errors are escalated instead (logged as error and rethrown), so this warn strictly means 'Chroma down or queue full at write time'.

Source

Thrown at src/services/sync/ChromaSync.ts:310

   * to advance their watermark, otherwise an interrupted backfill can mark
   * unsynced records as synced.
   *
   * Visibility: promoted from `private` to `public` for cmem-sdk Phase 6.
   * The SDK indexes Postgres observations into Chroma using this same
   * storage-agnostic document layer — same retry/dedupe semantics, same
   * BATCH_SIZE. SQLite-shaped `syncObservation` is NOT reusable for the
   * Postgres UUID path. See plan §6 line 244-247.
   */
  public async addDocuments(documents: ChromaDocument[]): Promise<number> {
    if (documents.length === 0) {
      return 0;
    }

    try {
      await this.ensureCollectionExists();
    } catch (error) {
      if (error instanceof ChromaUnavailableError) {
        logger.warn('CHROMA_SYNC', 'Chroma unavailable before write; leaving documents unsynced', {
          collection: this.collectionName,
          requested: documents.length,
          error: error.message
        });
        return 0;
      }
      const err = error instanceof Error ? error : new Error(String(error));
      logger.error('CHROMA_SYNC', 'Unexpected error ensuring collection before write', {
        collection: this.collectionName,
        requested: documents.length
      }, err);
      throw error;
    }

    const chromaMcp = ChromaMcpManager.getInstance();

    let written = 0;
    for (let i = 0; i < documents.length; i += this.BATCH_SIZE) {

View on GitHub (pinned to e2d1df569a)

Solutions

  1. Do nothing immediately — verify with a later backfill/sync run once isHealthy() returns true
  2. Check companion CHROMA_MCP warnings from the same timeframe to see why Chroma was unavailable
  3. If queue saturation is the cause, throttle ingestion or increase chroma throughput
  4. Re-run sync after chroma recovers and confirm document counts catch up

Example fix

// before
const synced = await chromaSync.addDocuments(docs); // 0 when chroma is down

// after — treat 0 as deferred and verify later
const synced = await chromaSync.addDocuments(docs);
if (synced < docs.length) {
  logger.info('CHROMA_SYNC', 'Deferred to backfill', { synced, total: docs.length });
  scheduleBackfill(); // re-drive sync once manager.isHealthy() is true
}
Defensive patterns

Strategy: fallback

Validate before calling

if (documents.length > 0 && (await chromaManager.isHealthy())) {
  await chromaSync.addDocuments(documents);
} else {
  scheduleBackfill(); // keep SQLite authoritative, sync later
}

Type guard

import { ChromaUnavailableError } from './errors';

function isChromaUnavailable(e: unknown): boolean {
  return e instanceof ChromaUnavailableError;
}

Try / catch

try {
  await chromaSync.addDocuments(docs);
} catch (err) {
  if (isChromaUnavailable(err)) {
    scheduleBackfill(); // deferred, not lost
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: chroma-mcp not connectable during a sync batch, or the mutation queue full so enqueueMutation throws ChromaUnavailableError while the documents were being added.

Common situations: Chroma startup lag on boot; chroma crashed mid-session; ingestion bursts saturating the mutation cap.

Related errors


AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20). Data as JSON: /api/errors/dd0d0a58395b883b. Report an issue: GitHub.