{"record":{"id":"c1c0914153269d85","repo":"abhigyanpatwari/GitNexus","slug":"worker-workerindex-parse-job-exhausted-cumulati","errorCode":null,"errorMessage":"Worker ${workerIndex} parse job exhausted cumulative timeout budget. Surfacing in-flight file(s).","messagePattern":"Worker (.+?) parse job exhausted cumulative timeout budget\\. Surfacing in-flight file\\(s\\)\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/ingestion/workers/worker-pool.ts","lineNumber":1857,"sourceCode":"        job: WorkerJob<TInput>,\n        lastProgress: number,\n        inFlightPath: string | undefined,\n      ): TimeoutDecision => {\n        const nextTimeout = Math.ceil(job.timeoutMs * poolOptions.timeoutBackoffFactor);\n        const nextCumulative = job.cumulativeTimeoutMs + nextTimeout;\n\n        // Layer 5: respect the per-job cumulative timeout budget. Once\n        // exhausted, surface the in-flight file via WorkerPoolDispatchError\n        // instead of letting exponential backoff stall further.\n        if (nextCumulative > poolOptions.maxCumulativeTimeoutMs) {\n          const firstPath = itemPath(job.items[0]);\n          const exhausted: string[] =\n            inFlightPath !== undefined\n              ? [inFlightPath]\n              : firstPath !== undefined\n                ? [firstPath]\n                : [];\n          logger.warn(\n            {\n              workerIndex,\n              cumulativeMs: job.cumulativeTimeoutMs,\n              nextCumulativeMs: nextCumulative,\n              maxCumulativeMs: poolOptions.maxCumulativeTimeoutMs,\n              exhausted,\n            },\n            `Worker ${workerIndex} parse job exhausted cumulative timeout budget. Surfacing in-flight file(s).`,\n          );\n          return {\n            kind: 'give-up',\n            reason:\n              `Worker ${workerIndex} parse job exhausted cumulative timeout budget ` +\n              `(${(nextCumulative / 1000).toFixed(0)}s > ${(poolOptions.maxCumulativeTimeoutMs / 1000).toFixed(0)}s cap)`,\n            excludePaths: exhausted,\n          };\n        }\n","sourceCodeStart":1839,"sourceCodeEnd":1875,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/0d1aed942f0e8b5d3bac27519fff441aceea722d/gitnexus/src/core/ingestion/workers/worker-pool.ts#L1839-L1875","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Raise GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS (and/or GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS) so the job can converge on your hardware","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","If the error aborts a large analyze run, re-run with the file excluded, then index it separately with a dedicated generous budget","Report systematic cases upstream with the budget trace (cumulativeMs → nextCumulativeMs → maxCumulativeMs) from the warn"],"exampleFix":"# before\nexport GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS=   # default budget exhausted → give-up error\n# after\nexport GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS=1800000\nnpx gitnexus analyze   # job converges inside the budget","handlingStrategy":"validation","validationCode":"// Catch the give-up error and surface its file list:\ntry {\n  await pool.dispatch(jobs);\n} catch (err) {\n  if (err instanceof WorkerPoolDispatchError) {\n    const budgetFiles = err.message.match(/exhausted cumulative timeout budget[\\s\\S]*/);\n    await writeLines('.gitnexusignore', extractPaths(budgetFiles?.[0] ?? ''));\n  }\n  throw err;\n}","typeGuard":null,"tryCatchPattern":"// Pattern: distinguish budget exhaustion (config) from poison files (input)\ntry {\n  await runPool();\n} catch (e) {\n  if (e instanceof WorkerPoolDispatchError && /cumulative timeout/i.test(e.message)) {\n    // raise GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS or exclude surfaced files, then retry once\n  } else throw e;\n}","preventionTips":["Scale GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS with your largest repo and slowest runner","Pre-split or exclude files that eat the whole budget — the surfaced in-flight list is the worklist","Keep the timeout family consistent: idle timeout, retries, backoff, and cumulative budget move together"],"tags":["worker-pool","timeout","budget-exhausted","parse-jobs","dispatch-error"],"backgroundTag":"job-timeout-budget-exceeded","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"}