abhigyanpatwari/GitNexus · warning

[node] package.json scan of ${repoRoot} hit the ${SCAN_MAX_D

Error message

[node] package.json scan of ${repoRoot} hit the ${SCAN_MAX_DIRS}-directory cap; workspace packages below it will not resolve

What it means

loadNodeWorkspacePackages BFS-scans repoRoot for package.json manifests to enable workspace import resolution; the scan stopped after SCAN_MAX_DIRS (20,000) directories. Workspace packages located below the unscanned portion of the tree will not resolve, degrading import and call resolution for them.

Source

Thrown at gitnexus/src/core/ingestion/import-resolvers/node-workspace-packages.ts:347

/**
 * Collect the `package.json` of every ADMITTED workspace package.
 *
 * Directory-only BFS: the sole files opened are manifests and the workspace
 * declaration, so this is far cheaper than the C# namespace scan next door,
 * which reads every `.cs` file.
 */
export async function loadNodeWorkspacePackages(
  repoRoot: string,
): Promise<NodeWorkspacePackages | null> {
  const scope = await loadWorkspaceScope(repoRoot);
  const byName = new Map<string, NodeWorkspacePackage>();
  const queue: { dir: string; depth: number }[] = [{ dir: repoRoot, depth: 0 }];
  let dirsScanned = 0;

  while (queue.length > 0) {
    if (dirsScanned >= SCAN_MAX_DIRS) {
      logger.warn(
        `[node] package.json scan of ${repoRoot} hit the ${SCAN_MAX_DIRS}-directory cap; workspace packages below it will not resolve`,
      );
      break;
    }
    const { dir, depth } = queue.shift()!;
    dirsScanned++;

    let entries: import('fs').Dirent[];
    try {
      entries = await fs.readdir(dir, { withFileTypes: true });
    } catch {
      continue;
    }

    for (const entry of entries) {
      if (entry.isDirectory()) {
        const childDir = path.join(dir, entry.name);
        if (isHardcodedIgnoredDirectoryAtPath(repoRoot, childDir)) continue;

View on GitHub (pinned to 52924ef12c)

Solutions

  1. Prune committed vendored trees / node_modules so the directory count drops under 20,000
  2. Add ignore rules so large generated/vendor subtrees are excluded from the walk
  3. Verify which packages failed to resolve and accept degraded resolution if the tree is legitimately above the cap

Example fix

# before — committed node_modules pushes repo to 45k dirs
repo/node_modules/**  (committed)

# after — remove and ignore it
git rm -r --cached node_modules
echo 'node_modules/' >> .gitignore
Defensive patterns

Strategy: validation

Validate before calling

import { readdirSync, statSync } from 'node:fs';

function countDirs(root: string): number {
  let n = 0;
  const walk = (dir: string) => {
    n++;
    if (n > 20_000) return;
    for (const e of readdirSync(dir, { withFileTypes: true })) {
      if (e.isDirectory()) walk(`${dir}/${e.name}`);
    }
  };
  walk(root);
  return n;
}
if (countDirs(repoRoot) >= 20_000) {
  throw new Error('directory count at/above the 20,000 scan cap — prune vendored trees or add ignore rules');
}

Type guard

function withinScanCap(dirCount: number, cap = 20_000): boolean {
  return dirCount < cap;
}

Prevention

When it happens

Trigger: A monorepo (or a repo containing committed vendored trees) with more than 20,000 directories under the scan root — the cap trips before the whole tree is enumerated and the loop breaks.

Common situations: Huge pnpm/yarn workspaces; repos that accidentally commit node_modules or vendored SDKs; symptom is workspace:* imports in far-down packages failing to resolve to real symbols.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@52924ef12c (2026-08-20). Data as JSON: /api/errors/87cdb8907c1333df. Report an issue: GitHub.