{"record":{"id":"214470681cd1c5d4","repo":"thedotmack/claude-mem","slug":"failed-to-kill-prior-chroma-mcp-tree-best-effort","errorCode":null,"errorMessage":"failed to kill prior chroma-mcp tree (best-effort)","messagePattern":"failed to kill prior chroma-mcp tree \\(best-effort\\)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/services/sync/ChromaMcpManager.ts","lineNumber":919,"sourceCode":"   * Idempotent and best-effort — safe to call when there is no active\n   * subprocess (no-op in that case).\n   */\n  private async disposeCurrentSubprocess(): Promise<void> {\n    await this.disposeActivePrewarm();\n\n    const closingTransport = this.transport;\n    if (closingTransport) {\n      this.intentionallyClosingTransports.add(closingTransport as unknown as object);\n    }\n\n    const chromaProcess = (this.transport as unknown as { _process?: ChildProcess })?._process;\n    const trackedPid = chromaProcess?.pid;\n\n    if (trackedPid) {\n      try {\n        await ChromaMcpManager.killProcessTree(trackedPid);\n      } catch (error) {\n        logger.warn('CHROMA_MCP', 'failed to kill prior chroma-mcp tree (best-effort)', {\n          pid: trackedPid,\n          error: error instanceof Error ? error.message : String(error)\n        });\n      }\n    }\n\n    if (closingTransport) {\n      try { await closingTransport.close(); } catch { /* already dead */ }\n    }\n    if (this.client) {\n      try { await this.client.close(); } catch { /* already dead */ }\n    }\n\n    if (trackedPid) {\n      getSupervisor().unregisterProcess(CHROMA_SUPERVISOR_ID);\n    }\n    this.releaseChromaWriterLock();\n","sourceCodeStart":901,"sourceCodeEnd":937,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/services/sync/ChromaMcpManager.ts#L901-L937","documentation":"During disposal of the current chroma-mcp connection, killProcessTree(pid) threw and was logged as best-effort. Shutdown continues (transport.close, client.close, direct SIGKILL paths), but a failed tree-kill risks orphaned uv/python descendants — the Linux zombie problem (#2313) this disposal path exists to prevent.","triggerScenarios":"The pid was already reaped by another cleaner (ESRCH race), permission denied on a process owned by a different user after a privilege change, or the process exiting between pid capture and the kill call.","commonSituations":"Rapid reconnect cycles; process supervisors (systemd) reaping children first; containers with restricted process permissions.","solutions":["Check for orphans afterwards: pgrep -af 'chroma-mcp|uvx' and kill any leftovers manually","Treat as best-effort — the manager completes shutdown regardless","If orphans accumulate repeatedly, upgrade claude-mem (#2313-era fixes) and stop the worker with SIGTERM so disposal paths can run"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"import { execSync } from 'node:child_process';\n\nfunction findChromaOrphans(): string[] {\n  if (process.platform !== 'linux') return [];\n  try {\n    return execSync(\"pgrep -af 'chroma-mcp|uvx'\").toString().trim().split(String.fromCharCode(10));\n  } catch {\n    return []; // pgrep exits 1 when nothing matches\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["After unclean shutdowns, run pgrep -af 'chroma-mcp|uvx' and kill leftovers","Stop the worker with SIGTERM so disposal paths get to run","Report recurring orphan accumulation upstream — it indicates the tree-kill is racing an external reaper"],"tags":["chroma","process-cleanup","zombie-process","shutdown"],"backgroundTag":"process-kill-failed","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}