{"record":{"id":"7e8cb788062871d0","repo":"abhigyanpatwari/GitNexus","slug":"analyzer-runtime-payload-scan-exceeded-limits-ru-7e8cb7","errorCode":null,"errorMessage":"Analyzer runtime payload scan exceeded ${limits.runtimePayloads} payloads: ${root}","messagePattern":"Analyzer runtime payload scan exceeded (.+?) payloads: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/analyzer-identity.ts","lineNumber":1378,"sourceCode":"        // for every name, not just the pruned four — `dist -> build`, a\n        // vendored-grammar link, anything a sibling checkout ships.\n        //\n        // Such links are RECORDED by their link text rather than followed.\n        // Following them would (a) recurse without cycle protection — this\n        // traversal has none, so `self -> .` would ride the depth limit, which\n        // THROWS, trading one hard abort for another; (b) re-scan trees already\n        // reached by their real path, inflating the entry/byte budgets that\n        // also throw; and (c) need a whole containment/TOCTOU trust boundary\n        // for targets outside the package. Recording the text is cycle-free,\n        // costs one `readlink`, and still moves the receipt when the link is\n        // retargeted. The trade-off is that a link's target contributes no\n        // content of its own: when it points outside the package, only the\n        // link text is covered. Links that DO resolve to a regular file keep\n        // their content digest below, unchanged.\n        if (shouldHashRuntimePayload(relativePath)) {\n          budget.artifacts += 1;\n          if (budget.artifacts > limits.runtimePayloads) {\n            throw new Error(\n              `Analyzer runtime payload scan exceeded ${limits.runtimePayloads} payloads: ${root}`,\n            );\n          }\n          artifacts.push({\n            absolutePath,\n            canonicalPath: `${canonicalPrefix}/${relativePath}`,\n            kind: 'unfollowed-symlink',\n          });\n        }\n      } else if (\n        (stat.isFile() || stat.isSymbolicLink()) &&\n        shouldHashRuntimePayload(relativePath)\n      ) {\n        const readableState = snapshotReadableFile(absolutePath);\n        const payloadBytes = stateSize(readableState.target, absolutePath);\n        budget.artifacts += 1;\n        if (budget.artifacts > limits.runtimePayloads) {\n          throw new Error(","sourceCodeStart":1360,"sourceCodeEnd":1396,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus/src/core/analyzer-identity.ts#L1360-L1396","documentation":"Thrown from the UNFOLLOWED-SYMLINK branch of collectArtifacts (the `stat.isSymbolicLink() && !isFile()` arm) when budget.artifacts crosses limits.runtimePayloads (default 100_000). This arm records symbolic links that point at non-regular-file targets (directories, dangling links, or out-of-package links) by their link text rather than by following them; the cap bounds how many such links the identity receipt will carry.","triggerScenarios":"A package tree containing a very large number of directory/dangling/non-file symbolic links that are NOT pruned (i.e. not named node_modules/.git/.hg/.svn) and not resolvable to a regular file. Each such link, if shouldHashRuntimePayload(relativePath) is true, increments budget.artifacts and pushes an 'unfollowed-symlink' RuntimeArtifact; crossing 100k throws.","commonSituations":"A monorepo that symlink-links many sibling workspace package directories (workspace linking via `npm link` / pnpm's symlink layout) creating thousands of cross-package directory symlinks; a vendored-grammar install that ships many dangling links after a partial build; a container bind-mount presenting the build tree with many overlay symlinks.","solutions":["Count non-file symlinks: `find <packageRoot> -type l ! -xtype f | wc -l` and identify the source directory.","Reduce workspace symlinks: prefer a hoisted node_modules or `npm install --no-links` / pnpm `node-linker=hoisted` for the analyzer run.","Repair dangling links: re-run the install/build that created them so they resolve, or remove them.","If a build step generates the links, run the analyzer before that step or against a production install."],"exampleFix":"// before: pnpm creates 120k cross-package directory symlinks\n//   -> \"Analyzer runtime payload scan exceeded 100000 payloads: /pkg\"\n//\n// after: hoist node_modules for the analyzer invocation\n//   $ pnpm install --node-linker=hoisted\n//   $ node .gitnexus/run.cjs analyze --index-only","handlingStrategy":"validation","validationCode":"const { execSync } = require('node:child_process');\nfunction assertUnfollowedSymlinkCountFeasible(packageRoot, limit = 95_000) {\n  // Count symlinks that do NOT resolve to a regular file (the 'unfollowed' arm).\n  let out;\n  try {\n    out = execSync(\n      `find . -type l ! -xtype f -not -path '*/node_modules/*' | wc -l`,\n      { cwd: packageRoot, stdio: ['ignore','pipe','ignore'] }\n    ).toString().trim();\n  } catch { return; }\n  const count = Number(out);\n  if (Number.isFinite(count) && count > limit) {\n    throw new Error(`Unfollowed-symlink count ${count} near the 100000 payload limit; reduce workspace links.`);\n  }\n}\n// assertUnfollowedSymlinkCountFeasible(process.cwd());","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefer a hoisted node_modules (`pnpm install --node-linker=hoisted`) for the analyzer run to avoid tens of thousands of cross-package symlinks.","Repair dangling symlinks after partial installs/builds.","Count non-file symlinks before invoking: `find . -type l ! -xtype f | wc -l`.","The runtimePayloads cap is shared with regular files; a large symlink farm plus a normal source tree can both contribute."],"tags":["analyzer-identity","filesystem","symlink","budget-limit"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}