{"record":{"id":"ebb2b8dbd9f4da1d","repo":"abhigyanpatwari/GitNexus","slug":"analyze-worker-crashed-code-code-retry-j-r","errorCode":null,"errorMessage":"Analyze worker crashed (code ${code}), retry ${j.retryCount}/${MAX_WORKER_RETRIES} in ${delay}ms","messagePattern":"Analyze worker crashed \\(code (.+?)\\), retry (.+?)/(.+?) in (.+?)ms","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"gitnexus/src/server/analyze-launch.ts","lineNumber":304,"sourceCode":"\n      child.on('error', (err) => {\n        releaseRepoLock(analyzeLockKey);\n        jobManager.updateJob(job.id, {\n          status: 'failed',\n          error: `Worker process error: ${err.message}`,\n        });\n      });\n\n      child.on('exit', (code) => {\n        const j = jobManager.getJob(job.id);\n        if (!j || isTerminalJobStatus(j.status)) return;\n\n        // Worker crashed — attempt retry if under the limit\n        if (j.retryCount < MAX_WORKER_RETRIES) {\n          j.retryCount++;\n          const delay = 1000 * Math.pow(2, j.retryCount - 1); // 1s, 2s\n          const lastErr = stderrChunks.trim().split('\\n').pop() || '';\n          logger.warn(\n            `Analyze worker crashed (code ${code}), retry ${j.retryCount}/${MAX_WORKER_RETRIES} in ${delay}ms` +\n              (lastErr ? `: ${lastErr}` : ''),\n          );\n          jobManager.updateJob(job.id, {\n            status: 'analyzing',\n            progress: {\n              phase: 'retrying',\n              percent: j.progress.percent,\n              message: `Worker crashed, retrying (${j.retryCount}/${MAX_WORKER_RETRIES})...`,\n            },\n          });\n          stderrChunks = '';\n          setTimeout(forkWorker, delay);\n        } else {\n          // Exhausted retries — permanent failure\n          releaseRepoLock(analyzeLockKey);\n          jobManager.updateJob(job.id, {\n            status: 'failed',","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/0d1aed942f0e8b5d3bac27519fff441aceea722d/gitnexus/src/server/analyze-launch.ts#L286-L322","documentation":"The out-of-process analyze worker (a spawned child doing the actual indexing) exited unexpectedly while its job was still active. The server catches the child's exit, logs the exit code plus the last stderr line the worker printed, and relaunches it with exponential backoff (1s then 2s, at most MAX_WORKER_RETRIES=2 attempts), keeping the job in 'analyzing' with progress phase 'retrying'. The job itself is not failed unless retries are exhausted.","triggerScenarios":"child.on('exit') fires with a non-zero or null/signal code while jobManager.getJob(job.id) is non-terminal and j.retryCount < 2. Concrete producers: the worker being OOM-killed, a segfault in a native tree-sitter grammar, a missing native module the worker require()s at startup, or an external process (CI, cgroup, supervisor) killing the child mid-run.","commonSituations":"Indexing very large repos where the worker heap exceeds Node limits; a broken optional tree-sitter grammar build (kotlin/dart/swift) crashing at load; containers with tight memory limits; CI runners that kill long-running child processes.","solutions":["Read the stderr line appended to the warning — it names the worker's actual crash cause. Address that first (e.g. raise the heap via NODE_OPTIONS=--max-old-space-size=8192, or disable a crash-prone optional grammar with GITNEXUS_SKIP_OPTIONAL_GRAMMARS=1).","If the crash is memory-driven, re-run analyze with more headroom or on a smaller scope/subdirectory.","If something external kills the child (CI timeouts, oom-killer, supervisor), stop it from doing so or run analyze directly instead of via serve.","If both retries exhaust and the job flips to failed, fix the root cause from the stderr line, then re-run `node .gitnexus/run.cjs analyze --index-only`."],"exampleFix":"# before: worker OOMs at default heap, retries twice, then fails\nnode .gitnexus/run.cjs analyze\n\n# after: give the worker room and skip crash-prone optional grammars\nNODE_OPTIONS=--max-old-space-size=8192 GITNEXUS_SKIP_OPTIONAL_GRAMMARS=1 \\\n  node .gitnexus/run.cjs analyze","handlingStrategy":"retry","validationCode":"// Before treating a job as done, confirm a terminal status — a crashed\n// worker keeps the job in 'analyzing' with progress.phase 'retrying'.\nconst job = jobManager.getJob(jobId);\nif (job && !isTerminalJobStatus(job.status)) {\n  // still running or retrying — keep polling instead of assuming success\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Give the analyze worker adequate heap on large repos (NODE_OPTIONS=--max-old-space-size=...).","Keep optional tree-sitter grammars healthy or set GITNEXUS_SKIP_OPTIONAL_GRAMMARS=1.","Watch for phase:'retrying' in job progress and investigate the appended stderr line on the first retry, not after exhaustion.","In CI, ensure nothing kills child processes (no per-process timeouts below analyze duration)."],"tags":["worker","child-process","retry","oom","native-module","analyze"],"backgroundTag":"worker-process-crash","analyzedSha":"0d1aed942f0e8b5d3bac27519fff441aceea722d","analyzedAt":"2026-08-20T23:29:22.980Z","contentChangedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}