{"record":{"id":"c0d2b8b7968b326a","repo":"thedotmack/claude-mem","slug":"queued-chroma-mcp-mutation-cancelled-during-shutdown","errorCode":null,"errorMessage":"queued chroma-mcp mutation cancelled during shutdown","messagePattern":"queued chroma-mcp mutation cancelled during shutdown","errorType":"exception","errorClass":"ChromaMcpConnectionCancelledError","httpStatus":null,"severity":"info","filePath":"src/services/sync/ChromaMcpManager.ts","lineNumber":900,"sourceCode":"\n  private async enqueueMutation<T>(operation: () => Promise<T>, toolName: string): Promise<T> {\n    if (this.pendingMutationCalls >= this.maxPendingMutationCalls) {\n      const message = `Chroma mutation queue is full (${this.pendingMutationCalls}/${this.maxPendingMutationCalls}); deferring \"${toolName}\" to a later backfill`;\n      logger.warn('CHROMA_MCP', message, {\n        toolName,\n        pendingMutations: this.pendingMutationCalls,\n        maxPendingMutations: this.maxPendingMutationCalls\n      });\n      throw new ChromaUnavailableError(message);\n    }\n\n    this.pendingMutationCalls += 1;\n    const enqueuedGeneration = this.connectionGeneration;\n    const run = this.mutationTail\n      .catch(() => undefined)\n      .then(async () => {\n        if (enqueuedGeneration !== this.connectionGeneration) {\n          throw new ChromaMcpConnectionCancelledError('queued chroma-mcp mutation cancelled during shutdown');\n        }\n        return operation();\n      });\n\n    this.mutationTail = run.then(() => undefined, () => undefined);\n\n    try {\n      return await run;\n    } finally {\n      this.pendingMutationCalls -= 1;\n    }\n  }\n\n  private static isMutationTool(toolName: string): boolean {\n    return CHROMA_MUTATION_TOOL_PATTERN.test(toolName);\n  }\n\n  async isHealthy(): Promise<boolean> {","sourceCodeStart":882,"sourceCodeEnd":918,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/services/sync/ChromaMcpManager.ts#L882-L918","documentation":"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.","triggerScenarios":"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.","commonSituations":"App shutdown with sync still writing, Chroma MCP server restart forcing reconnection, or a connection reset while many mutations are still queued.","solutions":["Catch ChromaMcpConnectionCancelledError and treat the mutation as not-applied; re-issue it after the new connection is ready.","Ensure a backfill runs after restart so cancelled mutations are re-applied.","Gate mutation calls on manager connection state (skip enqueueing during shutdown).","Verify only one manager instance owns the connection; concurrent shutdown/restart of two instances races generations."],"exampleFix":"// before\nawait manager.mutate('update', doc);\n// after\ntry {\n  await manager.mutate('update', doc);\n} catch (e) {\n  if (e instanceof ChromaMcpConnectionCancelledError) {\n    await manager.waitUntilConnected();\n    await manager.mutate('update', doc);\n  } else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// skip enqueueing during teardown\nif (!manager.isConnected() || manager.isShuttingDown()) {\n  await deferToBackfill(operation);\n  return;\n}","typeGuard":"function isConnectionCancelled(e: unknown): e is ChromaMcpConnectionCancelledError {\n  return e instanceof ChromaMcpConnectionCancelledError;\n}","tryCatchPattern":"try {\n  await manager.mutate('update', doc);\n} catch (e) {\n  if (isConnectionCancelled(e)) {\n    retryQueue.push(doc); // re-issue on next connection\n  } else throw e;\n}","preventionTips":["Gate write paths on connection state; don't enqueue during shutdown","Always run a backfill after restart so cancelled mutations are re-applied","Handle ChromaMcpConnectionCancelledError explicitly wherever mutations are issued","Avoid multiple manager instances sharing one Chroma connection"],"tags":["shutdown","connection-lifecycle","cancellation","chroma"],"backgroundTag":"connection-reset-during-operation","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}