abhigyanpatwari/GitNexus · error

Analyzer runtime payload scan exceeded depth ${limits.runtim

Error message

Analyzer runtime payload scan exceeded depth ${limits.runtimeDepth}: ${absolutePath}

What it means

Thrown by collectArtifacts when a subdirectory would be enqueued at depth >= limits.runtimeDepth (default 64). The depth counter bounds symlink/cycle-induced recursion since this traversal has no visited-set; hitting the cap means the directory tree is deeper than 64 levels, which in practice signals a symlink cycle or an absurdly nested generated tree.

Source

Thrown at gitnexus/src/core/analyzer-identity.ts:1350

      const stat = lstatSync(absolutePath);
      // Nested dependencies are collected from their manifests as separate
      // packages. Only prune those separately traversed trees and VCS
      // metadata; generic cache/model directories can contain loadable code,
      // native addons, Wasm modules, or data consumed by the runtime.
      //
      // Pruning is decided by NAME alone. These four names never carry analyzer
      // payload in any form: `node_modules` is traversed separately through
      // `resolveDependencyPackageRoot` (which follows links and guards each
      // hop), and a `.git`/`.hg`/`.svn` entry is VCS metadata whether it is a
      // directory, a symbolic link into a shared store, or — inside a submodule
      // or linked worktree checkout — a regular file holding a gitdir pointer.
      // Hashing that pointer would make analyzer identity depend on where the
      // checkout happens to live, which is a false-stale source, not a
      // semantic input.
      if (PRUNED_RUNTIME_DIRECTORIES.has(entry.name)) continue;
      if (stat.isDirectory()) {
        if (depth >= limits.runtimeDepth) {
          throw new Error(
            `Analyzer runtime payload scan exceeded depth ${limits.runtimeDepth}: ${absolutePath}`,
          );
        }
        pending.push({ absoluteDir: absolutePath, depth: depth + 1 });
      } else if (stat.isSymbolicLink() && !isFile(absolutePath)) {
        // A symbolic link that does not resolve to a regular file must never
        // reach the payload branch below: `snapshotReadableFile` stats the
        // target, and a directory (or a dangling link) makes it throw, aborting
        // the entire analyze. Workspace-linked checkouts made this reachable
        // 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

View on GitHub (pinned to d540b00184)

Solutions

  1. Inspect the named absolutePath and its ancestors for a symlink cycle: `ls -la <absolutePath>` and walk up with `readlink -f`.
  2. Repair node_modules: `rm -rf node_modules && npm install` (or pnpm/yarn equivalent) to remove the recursive structure.
  3. If the depth is real (generated tree), flatten or relocate it outside the scanned root.
  4. Confirm no `fs.symlink` in your build created a self-referential directory link.

Example fix

// before: dist/.cache -> dist  (self-referential directory symlink)
//   -> "Analyzer runtime payload scan exceeded depth 64: /pkg/dist/.cache/x..."
//
// after: remove the cycle
//   $ find node_modules -type l -a -path '*dist*' -delete
//   $ rm -rf node_modules && npm install
Defensive patterns

Strategy: try-catch

Validate before calling

const { execSync } = require('node:child_process');
function detectSymlinkCycle(packageRoot) {
  try {
    // find directory symlinks; a self/mutual reference indicates a cycle
    execSync('find . -type l -xtype d', { cwd: packageRoot, stdio: ['ignore','pipe','ignore'] });
  } catch { /* none */ }
}
// Or check depth explicitly:
function maxDirDepth(packageRoot, hardStop = 70) {
  const fs = require('node:fs'), path = require('node:path');
  let max = 0;
  function walk(d, depth) {
    max = Math.max(max, depth);
    if (depth >= hardStop) throw new Error(`Directory depth exceeds ${hardStop} at ${d}`);
    for (const e of fs.readdirSync(d, { withFileTypes: true })) {
      if (e.isDirectory() && !['node_modules','.git','.hg','.svn'].includes(e.name)) {
        walk(path.join(d, e.name), depth + 1);
      }
    }
  }
  walk(packageRoot, 0);
  return max;
}

Try / catch

try {
  identity = resolveAnalyzerRunnerIdentity(analyzerModuleUrl, options);
} catch (err) {
  if (err.message.includes('runtime payload scan exceeded depth')) {
    const p = err.message.split(': ').slice(1).join(': ');
    log.error(`Directory tree too deep (or cycled) at ${p}; rebuild node_modules.`);
  }
  throw err;
}

Prevention

When it happens

Trigger: collectArtifacts increments depth each time it pushes a child directory onto `pending`; if a directory at depth 64 still contains a subdirectory, the check `depth >= limits.runtimeDepth` fires on that child and the throw names the offending absolutePath.

Common situations: A symlink loop that does not resolve to a regular file but is itself reachable as a directory chain (e.g. self-referential or mutually-referential directory symlinks under a dependency); a deeply nested generated output like a sourcemap-of-sourcemap chain; a corrupt node_modules where a package re-installed itself recursively; very deep pnpm virtual-store paths combined with workspace nesting.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12). Data as JSON: /api/errors/ab32440166cfda69. Report an issue: GitHub.