abhigyanpatwari/GitNexus · warning

Worker ${workerIndex} timed out; retiring without immediate

Error message

Worker ${workerIndex} timed out; retiring without immediate terminate to avoid aborting native parser state.

What it means

When a worker exceeds its idle/parse timeout, the pool retires it WITHOUT an immediate terminate(): terminating a worker mid-native-parser call risks the process-wide N-API abort (#2432). Instead the worker record is marked safeToTerminate: false, a safe-point promise is captured, listeners are armed, the worker is unref'd, and this warn records the retirement with the worker index and reason.

Source

Thrown at gitnexus/src/core/ingestion/workers/worker-pool.ts:1564

          cleanupRetired();
        };
        const onRetiredMessageError = () => terminateWhenBackInJs();
        const record: RetiredWorkerRecord = {
          worker,
          workerIndex,
          reason,
          cleanup: cleanupRetired,
          terminate: terminateRetired,
          safeToTerminate: false,
          safePoint,
        };
        retiredWorkers.add(record);
        worker.on('message', onRetiredMessage);
        worker.once('error', onRetiredError);
        worker.once('exit', onRetiredExit);
        worker.once('messageerror', onRetiredMessageError);
        (worker as Worker & { unref?: () => void }).unref?.();
        logger.warn(
          { workerIndex, reason },
          `Worker ${workerIndex} timed out; retiring without immediate terminate to avoid aborting native parser state.`,
        );
      };

      const removeWorkerFromSlot = async (
        workerIndex: number,
        mode: WorkerRemovalMode,
        reason: string,
      ): Promise<void> => {
        const existing = workers[workerIndex];
        workers[workerIndex] = undefined;
        if (!existing) return;
        if (mode === 'retire') {
          retireWorkerAfterTimeout(existing, workerIndex, reason);
          return;
        }
        // Recovery must settle before dispatch returns, but a failed thread

View on GitHub (pinned to 0d1aed942f)

Solutions

  1. Identify the stalling file from the retirement reason / subsequent job warns and exclude or shrink it
  2. Raise GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS (or the configured idle timeout) if your hardware routinely needs longer per sub-batch
  3. Expect job-level handling to follow: the stalled job is split/retried/quarantined independently — this warn is about the worker, not the file outcome
  4. If retirements cluster on one language/grammar, check for a slow source-built native grammar and prefer the prebuilt binary

Example fix

# before
export GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS=   # default too low for big files → retire warn
# after
export GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS=120000   # sub-batches finish in-budget, no retire
Defensive patterns

Strategy: fallback

Validate before calling

// Budget timeouts to your hardware before the run:
const slowCi = Number(process.env.GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS ?? 0) < 60000;
if (slowCi) process.env.GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS = '120000';

Prevention

When it happens

Trigger: A parse job blowing its timeout budget on a worker — the retire path fires: the slot is replaced by a fresh worker while the old one is drained/awaited at its safe point rather than killed; the warn names workerIndex and the timeout reason.

Common situations: Parser-hostile files (deeply nested/generated code) stalling a worker past GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS, overloaded CI machines making legit parses look idle, or overly aggressive timeout overrides.

Understand the failure class

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@0d1aed942f (2026-08-20). Data as JSON: /api/errors/02b788eb12712ab1. Report an issue: GitHub.