thedotmack/claude-mem · error · ChromaUnavailableError

chroma-mcp prewarm failed

Error message

chroma-mcp prewarm failed: ${errorMessage}

What it means

Thrown by the ChromaMcpManager prewarm path when spawning/waiting for the chroma-mcp child (via uvx) fails or times out. The manager kills the child, records vector search as unavailable via recordUvxVectorSearchUnavailable, and throws ChromaUnavailableError with the underlying error message. Prewarming is the eager startup of the MCP server so later vector searches do not pay cold-start cost.

Solutions

  1. Run `uvx chroma-mcp --help` manually to verify uvx and the chroma-mcp package work outside the plugin; install/upgrade with `uv` if missing
  2. Increase the prewarm timeout or pre-warm once manually so the uvx package cache is populated, avoiding first-run download timeouts
  3. Check network/proxy settings if uvx is downloading the package (set HTTPS_PROXY, or pre-install chroma-mcp into the uv environment)
  4. Read the full error detail: the wrapped `error` in ChromaUnavailableError carries the child's stderr - fix the root cause it reports (e.g., invalid --data-dir, port conflicts)
  5. If the chroma data dir schema is from an older Chroma version, move/delete it so chroma-mcp can initialize a compatible one
Defensive patterns

Strategy: fallback

Validate before calling

import { execSync } from 'child_process';
function uvxAvailable(): boolean {
  try { execSync('uvx --version', { stdio: 'ignore' }); return true; } catch { return false; }
}
if (!uvxAvailable()) {
  // install uv first: curl -LsSf https://astral.sh/uv/install.sh | sh
}

Type guard

function isChromaUnavailableError(e: unknown): e is ChromaUnavailableError {
  return e instanceof ChromaUnavailableError;
}

Try / catch

try {
  await manager.connect();
} catch (e) {
  if (isChromaUnavailableError(e) && e.message.startsWith('chroma-mcp prewarm failed')) {
    // degrade: disable vector search features, keep the rest of the plugin working
    disableVectorSearchFeatures();
    logger.warn('Chroma prewarm failed; continuing without vector search', e.cause);
  } else throw e;
}

Prevention

When it happens

Trigger: chroma-mcp child exits with non-zero status, the prewarm timeout elapses before the server signals readiness, or spawning the uvx/chroma-mcp command fails (command missing, uv environment broken); errorMessage is the failure text embedded in this message.

Common situations: uv/uvx not installed or not on PATH; first-run uvx download of chroma-mcp package too slow and hits the prewarm timeout; Python/uv environment broken (uv python install failed); corporate proxy blocking package download; chroma-mcp version incompatible with the data dir schema.

Related errors


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

Appendix: source

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

      if (pid) {
        try {
          // Token from spawn time: by here the prewarm may already have exited
          // (that is often WHY we are in this branch), so self-capture would
          // read a replacement rather than the child we spawned.
          await killProcessTree(pid, { expectedStartToken: prewarmTracked?.startToken ?? null });
        } catch (killError) {
          logger.debug('CHROMA_MCP', 'prewarm process tree kill finished (best-effort)', {
            pid,
            error: killError instanceof Error ? killError.message : String(killError)
          });
        }
      } else {
        try { child.kill('SIGKILL'); } catch { /* already dead */ }
      }

      const unavailableMessage = `chroma-mcp prewarm failed: ${errorMessage}`;
      recordUvxVectorSearchUnavailable(unavailableMessage);
      throw new ChromaUnavailableError(unavailableMessage, error instanceof Error ? error : undefined);
    } finally {
      if (timeoutId) {
        clearTimeout(timeoutId);
      }
      if (this.activePrewarmChild === child) {
        this.activePrewarmChild = null;
        this.activePrewarmTracked = null;
      }
    }
  }

  async callTool(toolName: string, toolArguments: Record<string, unknown>): Promise<unknown> {
    if (!this.serializeMutations || !ChromaMcpManager.isMutationTool(toolName)) {
      return this.callToolUnqueued(toolName, toolArguments);
    }
    if (!this.acceptingLocalMutations) {
      throw new ChromaUnavailableError('Local Chroma mutations are unavailable after shutdown begins');
    }

View on GitHub (pinned to d8bc9755e7)