{"record":{"id":"efa6ad35ac10ae1c","repo":"abhigyanpatwari/GitNexus","slug":"worker-record-workerindex-is-still-inside-nativ","errorCode":null,"errorMessage":"Worker ${record.workerIndex} is still inside native code after the ${poolOptions.shutdownDrainMs}ms shutdown drain; leaving it un-terminated to avoid a native abort (#2432). It will be terminated at its next safe point.","messagePattern":"Worker (.+?) is still inside native code after the (.+?)ms shutdown drain; leaving it un-terminated to avoid a native abort \\(#2432\\)\\. It will be terminated at its next safe point\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"gitnexus/src/core/ingestion/workers/worker-pool.ts","lineNumber":1198,"sourceCode":"  };\n\n  const terminateTrackedWorkers = async (\n    liveWorkers: readonly (Worker | undefined)[],\n  ): Promise<void> => {\n    const retired = Array.from(retiredWorkers);\n    await Promise.all([\n      ...liveWorkers.map((worker) => worker?.terminate().catch(() => undefined)),\n      ...retired.map(async (record) => {\n        // #2432: a retired worker that has not reached a JS-visible safe\n        // point may be inside an N-API call — terminating it aborts the\n        // WHOLE process (`Napi::Error` → std::terminate → SIGABRT). Drain:\n        // wait (bounded) for its safe point; on expiry leave it running —\n        // it is unref'd and its at-safe-point terminate listener stays\n        // armed — and log which file wedged it.\n        if (!record.safeToTerminate) {\n          const drained = await settledWithin(record.safePoint, poolOptions.shutdownDrainMs);\n          if (!drained) {\n            logger.warn(\n              {\n                workerIndex: record.workerIndex,\n                reason: record.reason,\n                drainMs: poolOptions.shutdownDrainMs,\n              },\n              `Worker ${record.workerIndex} is still inside native code after the ` +\n                `${poolOptions.shutdownDrainMs}ms shutdown drain; leaving it un-terminated ` +\n                `to avoid a native abort (#2432). It will be terminated at its next safe point.`,\n            );\n            return;\n          }\n        }\n        await record.terminate();\n      }),\n    ]);\n    // Undrained records stay tracked so a repeated shutdown call can retry\n    // their (now possibly safe) terminate; record.terminate() removes each\n    // drained record via its cleanup.","sourceCodeStart":1180,"sourceCodeEnd":1216,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/0d1aed942f0e8b5d3bac27519fff441aceea722d/gitnexus/src/core/ingestion/workers/worker-pool.ts#L1180-L1216","documentation":"During pool shutdown, a retired worker that has not reached a JS-visible safe point may be inside an N-API (native parser) call; terminating it then aborts the whole process (Napi::Error → std::terminate → SIGABRT, #2432). The pool waits up to shutdownDrainMs for the worker's safe point; if the drain expires it leaves the worker un-terminated (unref'd, terminate listener still armed) and warns which worker wedged and why.","triggerScenarios":"Shutting down the pool while a worker is mid-parse inside native code that outlasts the drain window — a pathological file (deep parse, huge grammar tree) or a native slowdown; settledWithin(record.safePoint, shutdownDrainMs) returns false and the warn fires with workerIndex, reason, and drainMs.","commonSituations":"Indexing repos with parser-hostile files right before shutdown, tight GITNEXUS_WORKER_SHUTDOWN_DRAIN_MS overrides, slow/loaded CI machines stretching native parse time, or native grammar builds running slower than prebuilt ones.","solutions":["Raise GITNEXUS_WORKER_SHUTDOWN_DRAIN_MS so wedged workers can reach a safe point before the drain expires","Identify the wedging input from the warn's reason/context and exclude or shrink that file so native parse finishes promptly","Accept the behavior when benign: the worker is unref'd, so the process can still exit; it self-terminates at its next safe point","If processes hang after this warn, check for native leaks and report the file upstream with the drain stats"],"exampleFix":"# before\nexport GITNEXUS_WORKER_SHUTDOWN_DRAIN_MS=   # default drain expires → warn\n# after\nexport GITNEXUS_WORKER_SHUTDOWN_DRAIN_MS=30000   # native parse reaches safe point, clean exit","handlingStrategy":"fallback","validationCode":"// Give wedged native parses room before shutdown:\nif (!process.env.GITNEXUS_WORKER_SHUTDOWN_DRAIN_MS) {\n  process.env.GITNEXUS_WORKER_SHUTDOWN_DRAIN_MS = '30000';\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep shutdown drain generous on machines that index parser-hostile files","Exclude chronically wedging files so native parses finish within the drain","Rely on the pool's fallback: the un-terminated worker is unref'd and self-terminates at its next safe point — do not force-kill the process tree"],"tags":["worker-pool","shutdown","native-code","napi","process-abort-avoidance"],"backgroundTag":"worker-shutdown-timeout","analyzedSha":"0d1aed942f0e8b5d3bac27519fff441aceea722d","analyzedAt":"2026-08-20T23:29:22.980Z","contentChangedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}