thedotmack/claude-mem · warning · ChromaUnavailableError
Chroma mutation queue is full
Error message
Chroma mutation queue is full (${this.pendingMutationCalls}/${this.maxPendingMutationCalls}); deferring "${toolName}" to a later backfill What it means
ChromaMcpManager throttles mutation calls (add/update/delete to Chroma) through a serialized queue with a bounded number of pending operations. When pendingMutationCalls reaches maxPendingMutationCalls, further mutations are rejected with ChromaUnavailableError so they can be deferred to a later backfill instead of overwhelming the Chroma MCP server. This is deliberate backpressure, not a fault in the caller.
Solutions
- Wait for the in-flight queue to drain and retry the mutation; the deferred work is covered by a later backfill.
- Reduce concurrent producers of Chroma mutations (batch writes, serialize sync workers).
- Check Chroma MCP server health/latency; a slow server makes the bounded queue fill quickly.
- Raise maxPendingMutationCalls only if the Chroma server can absorb the extra load.
- Ensure a backfill pass runs afterwards (ChromaSync.ensureBackfilled) so deferred mutations are applied.
Example fix
// before: fire-and-forget bulk mutation loop
for (const doc of docs) await manager.mutate('add', doc);
// after: chunk and retry on ChromaUnavailableError
for (const chunk of chunked(docs, 100)) {
await Promise.all(chunk.map(d => manager.mutate('add', d).catch(e => {
if (e instanceof ChromaUnavailableError) deferred.push(d); else throw e;
})));
await backfillIfDeferred();
} Defensive patterns
Strategy: retry
Validate before calling
// before calling a mutation
if (manager.getPendingMutationCount() >= manager.maxPendingMutationCalls) {
await drainOrDeferToBackfill();
} Try / catch
try {
await manager.mutate('add', doc);
} catch (e) {
if (e instanceof ChromaUnavailableError && /queue is full/.test(e.message)) {
deferredDocs.push(doc); // re-applied by later backfill
} else throw e;
} Prevention
- Batch and pace Chroma mutation bursts instead of issuing them all at once
- Monitor pendingMutationCalls and apply backpressure to producers early
- Keep the Chroma MCP server healthy so the queue drains fast
- Run periodic backfill so deferred mutations are never lost
When it happens
Trigger: Calling any Chroma mutation via ChromaMcpManager while maxPendingMutationCalls mutations are already queued awaiting the serialized mutationTail promise; typically bursts of writes during heavy sync activity.
Common situations: Bulk document ingest during initial backfill, rapid re-indexing after schema changes, or a slow/unresponsive Chroma MCP server causing queued mutations to pile up faster than they drain.
Related errors
- Backfill failed
- Chroma data dir is already owned by PID ; refusing to start…
- chroma-mcp call cancelled during shutdown
- chroma-mcp connection cancelled during shutdown
- chroma-mcp connection failed
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/ec5055917cb71206.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sync/ChromaMcpManager.ts:891
if (parseError instanceof Error) {
logger.debug('CHROMA_MCP', 'Non-JSON response from tool, returning null', {
toolName,
textPreview: firstTextContent.text.slice(0, 100)
});
}
return null;
}
}
private async enqueueMutation<T>(operation: () => Promise<T>, toolName: string): Promise<T> {
if (this.pendingMutationCalls >= this.maxPendingMutationCalls) {
const message = `Chroma mutation queue is full (${this.pendingMutationCalls}/${this.maxPendingMutationCalls}); deferring "${toolName}" to a later backfill`;
logger.warn('CHROMA_MCP', message, {
toolName,
pendingMutations: this.pendingMutationCalls,
maxPendingMutations: this.maxPendingMutationCalls
});
throw new ChromaUnavailableError(message);
}
this.pendingMutationCalls += 1;
const enqueuedGeneration = this.connectionGeneration;
const run = this.mutationTail
.catch(() => undefined)
.then(async () => {
if (enqueuedGeneration !== this.connectionGeneration) {
throw new ChromaMcpConnectionCancelledError('queued chroma-mcp mutation cancelled during shutdown');
}
return operation();
});
this.mutationTail = run.then(() => undefined, () => undefined);
try {
return await run;
} finally {View on GitHub (pinned to d8bc9755e7)