From 8a6799c5fffdb9cebf973996262ee2dc49c8ce6c Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Sun, 21 Jun 2026 07:42:15 +0000 Subject: [PATCH] fix(server): skip native close in the analyze worker to avoid the LadybugDB destructor crash (#2264 review P2-3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The forked server analyze worker runs runFullAnalysis then force-exits (process.exit(0)). With a real native close inside runFullAnalysis, the LadybugDB ClientContext destructor can double-free after --pdg writes and abort the worker BEFORE it sends 'complete', failing the parent's analyze. Pass skipNativeCloseOnExit: true so the worker checkpoints for durability and lets its process.exit reclaim the handles — same about-to-exit contract as the CLI. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01JBJomjoTdBV2eveDVq4JMm --- gitnexus/src/server/analyze-worker.ts | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/gitnexus/src/server/analyze-worker.ts b/gitnexus/src/server/analyze-worker.ts index d14fb0d39..fb00b6502 100644 --- a/gitnexus/src/server/analyze-worker.ts +++ b/gitnexus/src/server/analyze-worker.ts @@ -74,14 +74,23 @@ process.on('message', async (msg: StartMessage) => { started = true; try { - const result = await runFullAnalysis(msg.repoPath, msg.options, { - onProgress: (phase, percent, message) => { - send({ type: 'progress', phase, percent, message }); + const result = await runFullAnalysis( + msg.repoPath, + // This worker force-exits (process.exit(0) below) right after sending its + // result, so skip the native close: it can double-free in LadybugDB's + // ClientContext destructor after --pdg writes and abort the worker BEFORE it + // can send 'complete' (#2264). flushWAL still persists the index; the + // subsequent process.exit reclaims the native handles. + { ...msg.options, skipNativeCloseOnExit: true }, + { + onProgress: (phase, percent, message) => { + send({ type: 'progress', phase, percent, message }); + }, + onLog: (message) => { + send({ type: 'progress', phase: 'log', percent: -1, message }); + }, }, - onLog: (message) => { - send({ type: 'progress', phase: 'log', percent: -1, message }); - }, - }); + ); // Send a JSON-safe projection, NOT the raw result: the IPC channel is // default-JSON serialization and `result.pipelineResult` carries the live