{"record":{"id":"e0fafe98fe4107df","repo":"thedotmack/claude-mem","slug":"chroma-mcp-connection-cancelled-during-shutdown","errorCode":null,"errorMessage":"chroma-mcp connection cancelled during shutdown","messagePattern":"chroma-mcp connection cancelled during shutdown","errorType":"exception","errorClass":"ChromaMcpConnectionCancelledError","httpStatus":null,"severity":"info","filePath":"src/services/sync/ChromaMcpManager.ts","lineNumber":398,"sourceCode":"      logger.debug('CHROMA_MCP', 'Background tree-kill after onclose finished (best-effort)', {\n        pid,\n        error: error instanceof Error ? error.message : String(error)\n      });\n    } finally {\n      this.releaseChromaWriterLock();\n    }\n  }\n\n  private async waitForUnexpectedCloseCleanup(): Promise<void> {\n    const cleanup = this.unexpectedCloseCleanup;\n    if (cleanup) {\n      await cleanup;\n    }\n  }\n\n  private assertConnectionNotCancelled(connectionGeneration: number): void {\n    if (this.connectionGeneration !== connectionGeneration) {\n      throw new ChromaMcpConnectionCancelledError();\n    }\n  }\n\n  private getLocalPersistentChromaDataDir(): string | null {\n    const settings = SettingsDefaultsManager.loadFromFile(USER_SETTINGS_PATH);\n    const chromaMode = settings.CLAUDE_MEM_CHROMA_MODE || 'local';\n    return chromaMode === 'remote' ? null : paths.chroma();\n  }\n\n  private buildCommandArgs(localChromaDataDir: string | null = this.getLocalPersistentChromaDataDir()): string[] {\n    const settings = SettingsDefaultsManager.loadFromFile(USER_SETTINGS_PATH);\n    const pythonVersion = process.env.CLAUDE_MEM_PYTHON_VERSION || settings.CLAUDE_MEM_PYTHON_VERSION || '3.13';\n    const launcherPrefix = ChromaMcpManager.buildLauncherPrefix(pythonVersion);\n\n    if (!localChromaDataDir) {\n      const chromaHost = settings.CLAUDE_MEM_CHROMA_HOST || '127.0.0.1';\n      const chromaPort = settings.CLAUDE_MEM_CHROMA_PORT || '8000';\n      const chromaSsl = settings.CLAUDE_MEM_CHROMA_SSL === 'true';","sourceCodeStart":380,"sourceCodeEnd":416,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/services/sync/ChromaMcpManager.ts#L380-L416","documentation":"assertConnectionNotCancelled compares the connection generation token passed into the connect flow against this.connectionGeneration. If another thread/code path bumped the generation (e.g. a shutdown or dispose started a new cycle), the in-flight connection is stale and ChromaMcpConnectionCancelledError is thrown to abandon it.","triggerScenarios":"Calling connect() and then disposing/restarting the manager (shutdown, settings reload, generation bump) before the handshake completes; prewarmChromaMcp or connectInternal checkpoints detect the generation mismatch.","commonSituations":"App shutdown racing a background reconnect; hot-reloading configuration that tears down and recreates ChromaMcpManager while a connect is in flight; rapid open/close cycles in tests.","solutions":["No fix needed — this is expected cancellation; catch ChromaMcpConnectionCancelledError and drop the pending connect.","Avoid disposing/restarting the manager while connect() is awaiting; await or cancel connections explicitly first.","If it fires unexpectedly, audit code paths that increment connectionGeneration (dispose, shutdown, reconnect) for races."],"exampleFix":"// before\nconst p = manager.connect();\nmanager.dispose(); // cancels in-flight connect -> throws\nawait p;\n// after\nconst p = manager.connect();\nawait p.catch(e => { if (!(e instanceof ChromaMcpConnectionCancelledError)) throw e; });\nmanager.dispose();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await manager.connect();\n} catch (e) {\n  if (e instanceof ChromaMcpConnectionCancelledError) {\n    return; // expected during shutdown/restart; drop the attempt\n  }\n  throw e;\n}","preventionTips":["Do not dispose or restart the manager while a connect() is in flight.","Await pending connections before shutdown, or treat cancellation as a no-op.","Keep connection-generation bumps (settings reload, dispose) serialized with connect calls."],"tags":["chroma","cancellation","lifecycle","race"],"backgroundTag":"invalid-state-transition","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"}