{"record":{"id":"dd0d0a58395b883b","repo":"thedotmack/claude-mem","slug":"chroma-unavailable-before-write-leaving-documents","errorCode":null,"errorMessage":"Chroma unavailable before write; leaving documents unsynced","messagePattern":"Chroma unavailable before write; leaving documents unsynced","errorType":"console","errorClass":"ChromaUnavailableError","httpStatus":null,"severity":"warning","filePath":"src/services/sync/ChromaSync.ts","lineNumber":310,"sourceCode":"   * to advance their watermark, otherwise an interrupted backfill can mark\n   * unsynced records as synced.\n   *\n   * Visibility: promoted from `private` to `public` for cmem-sdk Phase 6.\n   * The SDK indexes Postgres observations into Chroma using this same\n   * storage-agnostic document layer — same retry/dedupe semantics, same\n   * BATCH_SIZE. SQLite-shaped `syncObservation` is NOT reusable for the\n   * Postgres UUID path. See plan §6 line 244-247.\n   */\n  public async addDocuments(documents: ChromaDocument[]): Promise<number> {\n    if (documents.length === 0) {\n      return 0;\n    }\n\n    try {\n      await this.ensureCollectionExists();\n    } catch (error) {\n      if (error instanceof ChromaUnavailableError) {\n        logger.warn('CHROMA_SYNC', 'Chroma unavailable before write; leaving documents unsynced', {\n          collection: this.collectionName,\n          requested: documents.length,\n          error: error.message\n        });\n        return 0;\n      }\n      const err = error instanceof Error ? error : new Error(String(error));\n      logger.error('CHROMA_SYNC', 'Unexpected error ensuring collection before write', {\n        collection: this.collectionName,\n        requested: documents.length\n      }, err);\n      throw error;\n    }\n\n    const chromaMcp = ChromaMcpManager.getInstance();\n\n    let written = 0;\n    for (let i = 0; i < documents.length; i += this.BATCH_SIZE) {","sourceCodeStart":292,"sourceCodeEnd":328,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/services/sync/ChromaSync.ts#L292-L328","documentation":"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'.","triggerScenarios":"chroma-mcp not connectable during a sync batch, or the mutation queue full so enqueueMutation throws ChromaUnavailableError while the documents were being added.","commonSituations":"Chroma startup lag on boot; chroma crashed mid-session; ingestion bursts saturating the mutation cap.","solutions":["Do nothing immediately — verify with a later backfill/sync run once isHealthy() returns true","Check companion CHROMA_MCP warnings from the same timeframe to see why Chroma was unavailable","If queue saturation is the cause, throttle ingestion or increase chroma throughput","Re-run sync after chroma recovers and confirm document counts catch up"],"exampleFix":"// before\nconst synced = await chromaSync.addDocuments(docs); // 0 when chroma is down\n\n// after — treat 0 as deferred and verify later\nconst synced = await chromaSync.addDocuments(docs);\nif (synced < docs.length) {\n  logger.info('CHROMA_SYNC', 'Deferred to backfill', { synced, total: docs.length });\n  scheduleBackfill(); // re-drive sync once manager.isHealthy() is true\n}","handlingStrategy":"fallback","validationCode":"if (documents.length > 0 && (await chromaManager.isHealthy())) {\n  await chromaSync.addDocuments(documents);\n} else {\n  scheduleBackfill(); // keep SQLite authoritative, sync later\n}","typeGuard":"import { ChromaUnavailableError } from './errors';\n\nfunction isChromaUnavailable(e: unknown): boolean {\n  return e instanceof ChromaUnavailableError;\n}","tryCatchPattern":"try {\n  await chromaSync.addDocuments(docs);\n} catch (err) {\n  if (isChromaUnavailable(err)) {\n    scheduleBackfill(); // deferred, not lost\n    return;\n  }\n  throw err;\n}","preventionTips":["Check isHealthy() before large sync batches","Treat a 0 return from addDocuments as 'deferred', never as 'synced'","Keep a periodic backfill job so deferred documents converge"],"tags":["chroma","sync","deferred-write","backfill"],"backgroundTag":"chromadb-unavailable","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}