abhigyanpatwari/GitNexus · error
Analyzer runtime payload scan exceeded ${limits.runtimePaylo
Error message
Analyzer runtime payload scan exceeded ${limits.runtimePayloads} payloads: ${root} What it means
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.
Source
Thrown at gitnexus/src/core/analyzer-identity.ts:1378
// for every name, not just the pruned four — `dist -> build`, a
// vendored-grammar link, anything a sibling checkout ships.
//
// Such links are RECORDED by their link text rather than followed.
// Following them would (a) recurse without cycle protection — this
// traversal has none, so `self -> .` would ride the depth limit, which
// THROWS, trading one hard abort for another; (b) re-scan trees already
// reached by their real path, inflating the entry/byte budgets that
// also throw; and (c) need a whole containment/TOCTOU trust boundary
// for targets outside the package. Recording the text is cycle-free,
// costs one `readlink`, and still moves the receipt when the link is
// retargeted. The trade-off is that a link's target contributes no
// content of its own: when it points outside the package, only the
// link text is covered. Links that DO resolve to a regular file keep
// their content digest below, unchanged.
if (shouldHashRuntimePayload(relativePath)) {
budget.artifacts += 1;
if (budget.artifacts > limits.runtimePayloads) {
throw new Error(
`Analyzer runtime payload scan exceeded ${limits.runtimePayloads} payloads: ${root}`,
);
}
artifacts.push({
absolutePath,
canonicalPath: `${canonicalPrefix}/${relativePath}`,
kind: 'unfollowed-symlink',
});
}
} else if (
(stat.isFile() || stat.isSymbolicLink()) &&
shouldHashRuntimePayload(relativePath)
) {
const readableState = snapshotReadableFile(absolutePath);
const payloadBytes = stateSize(readableState.target, absolutePath);
budget.artifacts += 1;
if (budget.artifacts > limits.runtimePayloads) {
throw new Error(View on GitHub (pinned to d540b00184)
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.
Example fix
// before: pnpm creates 120k cross-package directory symlinks // -> "Analyzer runtime payload scan exceeded 100000 payloads: /pkg" // // after: hoist node_modules for the analyzer invocation // $ pnpm install --node-linker=hoisted // $ node .gitnexus/run.cjs analyze --index-only
Defensive patterns
Strategy: validation
Validate before calling
const { execSync } = require('node:child_process');
function assertUnfollowedSymlinkCountFeasible(packageRoot, limit = 95_000) {
// Count symlinks that do NOT resolve to a regular file (the 'unfollowed' arm).
let out;
try {
out = execSync(
`find . -type l ! -xtype f -not -path '*/node_modules/*' | wc -l`,
{ cwd: packageRoot, stdio: ['ignore','pipe','ignore'] }
).toString().trim();
} catch { return; }
const count = Number(out);
if (Number.isFinite(count) && count > limit) {
throw new Error(`Unfollowed-symlink count ${count} near the 100000 payload limit; reduce workspace links.`);
}
}
// assertUnfollowedSymlinkCountFeasible(process.cwd()); Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Analyzer runtime payload scan exceeded ${limits.runtimeEntri
- Analyzer runtime payload scan exceeded depth ${limits.runtim
- Analyzer runtime scan exceeded ${limits.runtimeBytes} bytes:
- Analyzer runtime payload scan exceeded ${limits.runtimeEntri
- Analyzer dependency graph exceeded ${limits.runtimeEdges} ed
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/7e8cb788062871d0.
Report an issue: GitHub.