{"record":{"id":"a1166c3d1935ae53","repo":"abhigyanpatwari/GitNexus","slug":"c-capture-extraction-exceeded-its-budgetms-ms","errorCode":null,"errorMessage":"C++ capture extraction exceeded its ${budgetMs}ms budget for ${filePath}; returning partial captures for this file (#2432).","messagePattern":"C\\+\\+ capture extraction exceeded its (.+?)ms budget for (.+?); returning partial captures for this file \\(#2432\\)\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"gitnexus/src/core/ingestion/languages/cpp/captures.ts","lineNumber":196,"sourceCode":"  // per file, 151s on a 194KB file that parses in 46ms. The index makes each\n  // lookup O(1) after a single lazily-built pass.\n  resetCppFileLookupIndex();\n\n  // Track ranges where typedef-struct/enum was captured as its concrete type\n  // so we can suppress the duplicate @declaration.typedef match.\n  const concreteTypedefRanges = new Set<string>();\n\n  // #2432: per-file deadline for the loop below (see cppCaptureBudgetMs).\n  // Checked every 64 matches — post-index a single iteration is microseconds,\n  // so the check granularity costs nothing and bounds the drift past the\n  // deadline to well under a second.\n  const budgetMs = cppCaptureBudgetMs();\n  const deadline = Date.now() + budgetMs;\n  let matchIndex = 0;\n\n  for (const m of rawMatches) {\n    if ((matchIndex++ & 63) === 0 && Date.now() >= deadline) {\n      logger.warn(\n        { filePath, budgetMs, processedMatches: matchIndex - 1, totalMatches: rawMatches.length },\n        `C++ capture extraction exceeded its ${budgetMs}ms budget for ${filePath}; returning partial captures for this file (#2432).`,\n      );\n      break;\n    }\n    const grouped: Record<string, Capture> = {};\n    // Parallel tag -> captured SyntaxNode map. The tree-sitter query already\n    // hands us each matched node as `c.node`, so anchors resolve via a\n    // type-guarded lookup (`nodeIfType`) instead of re-deriving them with\n    // `findNodeAtRange(tree.rootNode, ...)` per match — the\n    // O(matches × rootChildren) root-walk fixed for go #1848 / python #1918 /\n    // rust/csharp #1915 / java, mirrored here for C++ (#1951). Each C++\n    // scope-query anchor used below captures directly ON the node the old\n    // root-walk re-derived (verified against CPP_SCOPE_QUERY in query.ts and a\n    // real-parse AST probe), so the type check is exact.\n    const nodeMap: Record<string, SyntaxNode> = {};\n    for (const c of m.captures) {\n      const tag = '@' + c.name;","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/core/ingestion/languages/cpp/captures.ts#L178-L214","documentation":"Per-file wall-clock deadline for C++ tree-sitter capture extraction (default 20,000 ms via GITNEXUS_CPP_CAPTURE_BUDGET_MS; checked every 64 matches to bound drift). When the extraction loop blows the deadline — typically one pathological file with an enormous match count — it stops and returns partial captures for that file only (#2432), degrading symbol/relationship data for it.","triggerScenarios":"A single very large or dense C++ file (generated protobuf/UI headers, huge template-heavy sources) whose rawMatches exceed the budget; the env var set lower than needed; or a heavily loaded machine inflating wall-clock time.","commonSituations":"Indexing generated headers; CI runners under parallel load; symptom is incomplete C++ symbols or call edges for exactly one file while the rest of the index is fine.","solutions":["Raise the budget: export GITNEXUS_CPP_CAPTURE_BUDGET_MS=60000","Split the pathological file or exclude it via ignore rules if it is generated","Re-run analyze on a less loaded machine — load-induced overruns often clear"],"exampleFix":"# before — default 20s budget; huge generated.h times out\n\n# after\nexport GITNEXUS_CPP_CAPTURE_BUDGET_MS=60000\nnode .gitnexus/run.cjs analyze --index-only","handlingStrategy":"retry","validationCode":"import { statSync } from 'node:fs';\n\n// Pre-check the usual driver: one enormous generated C++ header\nconst budgetMs = parseInt(process.env.GITNEXUS_CPP_CAPTURE_BUDGET_MS ?? '20000', 10);\nconst bigHeaders = cppFiles.filter((f) => statSync(f).size > 2 * 1024 * 1024);\nif (bigHeaders.length > 0 && budgetMs <= 20_000) {\n  console.warn('large C++ files present with a tight capture budget:', bigHeaders);\n  // raise GITNEXUS_CPP_CAPTURE_BUDGET_MS or split/exclude before analyze\n}","typeGuard":"function captureBudgetLikelySufficient(fileSizeBytes: number, budgetMs = 20000): boolean {\n  // rough heuristic: ~100us per 1KB of dense generated header under normal load\n  return fileSizeBytes / 1024 * 0.1 <= budgetMs;\n}","tryCatchPattern":null,"preventionTips":["Set GITNEXUS_CPP_CAPTURE_BUDGET_MS higher (e.g. 60000) on machines that index giant generated headers","Split or exclude generated protobuf/UI headers that dwarf hand-written sources","Re-run analyze on a less loaded machine when the warning looks load-induced; partial captures only affect the named file"],"tags":["cpp","performance","time-budget","captures","env-var"],"backgroundTag":"time-budget-exceeded","analyzedSha":"aac7515d2a8c50a1f8f923c6fb77218b333560d6","analyzedAt":"2026-08-20T23:29:22.980Z","contentChangedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}