{"record":{"id":"0308b2bf9c92fe9a","repo":"abhigyanpatwari/GitNexus","slug":"analyzer-dependency-graph-exceeded-limits-runtim","errorCode":null,"errorMessage":"Analyzer dependency graph exceeded ${limits.runtimeEdges} edges: ${packageRoot}","messagePattern":"Analyzer dependency graph exceeded (.+?) edges: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/analyzer-identity.ts","lineNumber":1235,"sourceCode":"    const parent = queue[index];\n    // The declared half is enumerated for every package; the resolved-location\n    // half is scoped to the root package, where the 1998-resolution /\n    // 8829-extra-guard blow-up documented on\n    // `undeclaredLocalDevDependencyNames` cannot occur. Dropping this scope is\n    // the expensive regression, so it is pinned by a guard-count test.\n    const dependencies =\n      parent.root === packageRoot\n        ? [\n            ...new Set([\n              ...dependencyNames(parent.manifest),\n              ...undeclaredLocalDevDependencyNames(parent, pathGuards, limits),\n            ]),\n          ].sort(compareBytes)\n        : dependencyNames(parent.manifest);\n    for (const dependencyName of dependencies) {\n      budget.edges += 1;\n      if (budget.edges > limits.runtimeEdges) {\n        throw new Error(\n          `Analyzer dependency graph exceeded ${limits.runtimeEdges} edges: ${packageRoot}`,\n        );\n      }\n      const childRoot = resolveDependencyPackageRoot(\n        parent.root,\n        dependencyName,\n        pathGuards,\n        limits,\n      );\n      if (!childRoot) {\n        edges.push({\n          parentLocator: parent.locator,\n          parentLabel: parent.label,\n          dependencyName,\n          childLocator: '<missing>',\n          childLabel: '<missing>',\n        });\n        continue;","sourceCodeStart":1217,"sourceCodeEnd":1253,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus/src/core/analyzer-identity.ts#L1217-L1253","documentation":"Thrown by collectRuntimePackages when the cumulative edge count of the dependency graph traversal exceeds limits.runtimeEdges (default 100_000). Each declared dependency of each queued package increments budget.edges once; the throw is a hard ceiling that prevents a pathological or cyclic dependency closure from running unbounded work during analyzer-identity resolution.","triggerScenarios":"resolveAnalyzerRunnerIdentity() cold path (cache miss) -> collectDependencyInputs -> collectRuntimePackages, where the root package's dependency closure (declared deps plus undeclared-but-resolved local devDeps) is walked with a worklist. The counter ticks once per parent->dependencyName pair across every package; if it crosses 100k the loop aborts on the next iteration.","commonSituations":"A monorepo with thousands of transitive dependencies (e.g. a workspace hoisting many shared packages), a corrupted/oversized package-lock producing duplicate edges, or a development install that pulled in an enormous optional dependency tree (e.g. Playwright/Electron browsers). Also seen when node_modules is symlinked across many workspace siblings so the same dependency appears under many parents.","solutions":["Run `npm ls --all` / `pnpm why` in the package root to find the heaviest dependency trees and prune unused devDependencies that bloat the closure.","Shrink the install: remove bundled optionalDependencies or use `npm install --omit=optional` / `--production` for the environment running the analyzer.","If the closure is legitimately large, lower work by hoisting with a single node_modules root and removing duplicate workspace links.","As a last resort on a constrained host, pass options.traversalLimits.runtimeEdges (it is clamped to the default ceiling, never above) only to FAIL FASTER in tests — it cannot raise the production bound."],"exampleFix":"// before: every workspace package carries its own node_modules copy\n//   -> edge count explodes across duplicate closure walks\n//\n// after: hoist to a single root install\n//   $ rm -rf **/node_modules && npm install --workspaces --include-workspace-root\n// (root package.json \"workspaces\" field drives a single hoisted node_modules)","handlingStrategy":"validation","validationCode":"// Before resolving identity, sanity-check the dependency closure size.\nconst { execSync } = require('node:child_process');\nconst path = require('node:path');\nfunction assertEdgeBudgetFeasible(packageRoot) {\n  // Approximate edge count: sum of declared deps across the unique closure.\n  // `npm ls --all` prints one line per (package,parent) edge.\n  let out;\n  try { out = execSync('npm ls --all --parseable', { cwd: packageRoot, stdio: ['ignore','pipe','ignore'], maxBuffer: 64*1024*1024 }).toString(); }\n  catch (e) { out = e.stdout?.toString() ?? ''; }\n  const edgeEstimate = out.split('\\n').filter(Boolean).length;\n  if (edgeEstimate > 90_000) {\n    throw new Error(`Dependency edge estimate ${edgeEstimate} is near the 100000 analyzer limit; prune or dedupe first.`);\n  }\n}\n// assertEdgeBudgetFeasible(process.cwd());","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep dependency closures lean: run `npm ls --all` periodically and prune unused devDependencies.","Hoist dependencies to a single node_modules in workspaces to avoid duplicate closure walks.","Run the analyzer against a production install (--omit=dev) when possible to shrink the closure.","Note options.traversalLimits.runtimeEdges can only LOWER the cap (clamped via Math.min), never raise it — the 100k ceiling is a hard production bound."],"tags":["analyzer-identity","dependency-graph","budget-limit","traversal"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}