thedotmack/claude-mem · info · ChromaMcpConnectionCancelledError

queued chroma-mcp mutation cancelled during shutdown

Error message

queued chroma-mcp mutation cancelled during shutdown

What it means

Mutations enqueued in ChromaMcpManager capture the connectionGeneration at enqueue time. When the queued promise finally runs, if the generation has advanced (meaning shutdown or reconnect occurred), the stale operation is aborted with ChromaMcpConnectionCancelledError rather than being executed against a torn-down or replaced connection. This prevents mutations leaking across connection lifecycles.

Solutions

  1. Catch ChromaMcpConnectionCancelledError and treat the mutation as not-applied; re-issue it after the new connection is ready.
  2. Ensure a backfill runs after restart so cancelled mutations are re-applied.
  3. Gate mutation calls on manager connection state (skip enqueueing during shutdown).
  4. Verify only one manager instance owns the connection; concurrent shutdown/restart of two instances races generations.

Example fix

// before
await manager.mutate('update', doc);
// after
try {
  await manager.mutate('update', doc);
} catch (e) {
  if (e instanceof ChromaMcpConnectionCancelledError) {
    await manager.waitUntilConnected();
    await manager.mutate('update', doc);
  } else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// skip enqueueing during teardown
if (!manager.isConnected() || manager.isShuttingDown()) {
  await deferToBackfill(operation);
  return;
}

Type guard

function isConnectionCancelled(e: unknown): e is ChromaMcpConnectionCancelledError {
  return e instanceof ChromaMcpConnectionCancelledError;
}

Try / catch

try {
  await manager.mutate('update', doc);
} catch (e) {
  if (isConnectionCancelled(e)) {
    retryQueue.push(doc); // re-issue on next connection
  } else throw e;
}

Prevention

When it happens

Trigger: A mutation is waiting in mutationTail when ChromaMcpManager shuts down or reconnects (connectionGeneration increments); when the queued promise resumes it detects enqueuedGeneration !== connectionGeneration and throws.

Common situations: App shutdown with sync still writing, Chroma MCP server restart forcing reconnection, or a connection reset while many mutations are still queued.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17). Data as JSON: /api/errors/c0d2b8b7968b326a. Report an issue: GitHub.

Appendix: source

Thrown at src/services/sync/ChromaMcpManager.ts:900

  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 {
      this.pendingMutationCalls -= 1;
    }
  }

  private static isMutationTool(toolName: string): boolean {
    return CHROMA_MUTATION_TOOL_PATTERN.test(toolName);
  }

  async isHealthy(): Promise<boolean> {

View on GitHub (pinned to d8bc9755e7)