abhigyanpatwari/GitNexus · error

Worker ${workerIndex} parse job exhausted cumulative timeout

Error message

Worker ${workerIndex} parse job exhausted cumulative timeout budget. Surfacing in-flight file(s).

What it means

Layer 5 of timeout handling: each job accumulates its timeout budget across retries (cumulativeTimeoutMs + next backoff). When the next cumulative value would exceed maxCumulativeTimeoutMs (option, or GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS, defaulting to a multiple of the sub-batch idle timeout), the pool stops retrying: it warns with the budget numbers and surfaced files, then returns give-up so the failure becomes a WorkerPoolDispatchError instead of an infinite exponential-backoff stall.

Source

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

        job: WorkerJob<TInput>,
        lastProgress: number,
        inFlightPath: string | undefined,
      ): TimeoutDecision => {
        const nextTimeout = Math.ceil(job.timeoutMs * poolOptions.timeoutBackoffFactor);
        const nextCumulative = job.cumulativeTimeoutMs + nextTimeout;

        // Layer 5: respect the per-job cumulative timeout budget. Once
        // exhausted, surface the in-flight file via WorkerPoolDispatchError
        // instead of letting exponential backoff stall further.
        if (nextCumulative > poolOptions.maxCumulativeTimeoutMs) {
          const firstPath = itemPath(job.items[0]);
          const exhausted: string[] =
            inFlightPath !== undefined
              ? [inFlightPath]
              : firstPath !== undefined
                ? [firstPath]
                : [];
          logger.warn(
            {
              workerIndex,
              cumulativeMs: job.cumulativeTimeoutMs,
              nextCumulativeMs: nextCumulative,
              maxCumulativeMs: poolOptions.maxCumulativeTimeoutMs,
              exhausted,
            },
            `Worker ${workerIndex} parse job exhausted cumulative timeout budget. Surfacing in-flight file(s).`,
          );
          return {
            kind: 'give-up',
            reason:
              `Worker ${workerIndex} parse job exhausted cumulative timeout budget ` +
              `(${(nextCumulative / 1000).toFixed(0)}s > ${(poolOptions.maxCumulativeTimeoutMs / 1000).toFixed(0)}s cap)`,
            excludePaths: exhausted,
          };
        }

View on GitHub (pinned to 0d1aed942f)

Solutions

  1. Raise GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS (and/or GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS) so the job can converge on your hardware
  2. Take the surfaced in-flight file from the error/warn and split, shrink, or exclude it — a file that eats the whole budget will do so again
  3. If the error aborts a large analyze run, re-run with the file excluded, then index it separately with a dedicated generous budget
  4. Report systematic cases upstream with the budget trace (cumulativeMs → nextCumulativeMs → maxCumulativeMs) from the warn

Example fix

# before
export GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS=   # default budget exhausted → give-up error
# after
export GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS=1800000
npx gitnexus analyze   # job converges inside the budget
Defensive patterns

Strategy: validation

Validate before calling

// Catch the give-up error and surface its file list:
try {
  await pool.dispatch(jobs);
} catch (err) {
  if (err instanceof WorkerPoolDispatchError) {
    const budgetFiles = err.message.match(/exhausted cumulative timeout budget[\s\S]*/);
    await writeLines('.gitnexusignore', extractPaths(budgetFiles?.[0] ?? ''));
  }
  throw err;
}

Try / catch

// Pattern: distinguish budget exhaustion (config) from poison files (input)
try {
  await runPool();
} catch (e) {
  if (e instanceof WorkerPoolDispatchError && /cumulative timeout/i.test(e.message)) {
    // raise GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS or exclude surfaced files, then retry once
  } else throw e;
}

Prevention

When it happens

Trigger: A parse job whose timeouts keep escalating — split/retry loops that grow nextCumulative past the configured ceiling; the in-flight path (or items[0]'s path as fallback) is surfaced in the warn and error so the operator knows which file ate the budget.

Common situations: Gigantic or pathological files that always exceed even raised timeouts, chains of splits on CI machines so slow that backoff accumulates fast, or a maxCumulativeTimeoutMs override that is too tight for legitimately large repos.

Understand the failure class

Related errors


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