fix: close transport on connection error to prevent chroma-mcp zombie processes

Fixes #761

Root cause: When connection errors occur (MCP error -32000, Connection closed),
the code was resetting \`connected\` and \`client\` but NOT calling
\`transport.close()\`, leaving the chroma-mcp subprocess alive. Each
reconnection attempt spawned a NEW process while old ones accumulated.

Changes:
- Close transport before resetting state in ensureCollection() error handler
- Close transport before resetting state in queryChroma() error handler
- Set transport = null after closing to match close() method behavior
- Add regression tests for Issue #761 with source code verification

Tested on macOS - no more zombie processes after the fix.
This commit is contained in:
Jenha Poyarkov
2026-01-21 15:40:42 +00:00
committed by Alex Newman
parent b967772897
commit 9f2a237aaf
2 changed files with 100 additions and 0 deletions
+19
View File
@@ -200,9 +200,19 @@ export class ChromaSync {
errorMessage.includes('MCP error -32000');
if (isConnectionError) {
// FIX: Close transport to kill subprocess before resetting state
// Without this, old chroma-mcp processes leak as zombies
if (this.transport) {
try {
await this.transport.close();
} catch (closeErr) {
logger.debug('CHROMA_SYNC', 'Transport close error (expected if already dead)', {}, closeErr as Error);
}
}
// Reset connection state so next call attempts reconnect
this.connected = false;
this.client = null;
this.transport = null;
logger.error('CHROMA_SYNC', 'Connection lost during collection check',
{ collection: this.collectionName }, error as Error);
throw new Error(`Chroma connection lost: ${errorMessage}`);
@@ -860,9 +870,18 @@ export class ChromaSync {
errorMessage.includes('MCP error -32000');
if (isConnectionError) {
// FIX: Close transport to kill subprocess before resetting state
if (this.transport) {
try {
await this.transport.close();
} catch (closeErr) {
logger.debug('CHROMA_SYNC', 'Transport close error (expected if already dead)', {}, closeErr as Error);
}
}
// Reset connection state so next call attempts reconnect
this.connected = false;
this.client = null;
this.transport = null;
logger.error('CHROMA_SYNC', 'Connection lost during query',
{ project: this.project, query }, error as Error);
throw new Error(`Chroma query failed - connection lost: ${errorMessage}`);