abhigyanpatwari/GitNexus · error

Analyzer package lock is not a regular file: ${candidate}

Error message

Analyzer package lock is not a regular file: ${candidate}

What it means

If a candidate package-lock.json exists but lstat reports neither a regular file nor a symbolic link (a directory, FIFO, socket, device), the walk throws instead of skipping it. Skipping would mean an install whose frozen dependency state is unreadable still gets a 'stable' identity, which the receipt model forbids — the lockfile is a semantic input. ENOENT is the only swallowed case (normal 'no lockfile here' continuation).

Source

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

    try {
      const link = lstatSync(candidate);
      if (link.isFile()) return path.resolve(candidate);
      if (link.isSymbolicLink()) {
        try {
          const target = statSync(candidate);
          if (!target.isFile()) {
            throw new Error(
              `Analyzer package lock symbolic link does not resolve to a file: ${candidate}`,
            );
          }
        } catch {
          throw new Error(
            `Analyzer package lock symbolic link does not resolve to a file: ${candidate}`,
          );
        }
        return path.resolve(candidate);
      }
      throw new Error(`Analyzer package lock is not a regular file: ${candidate}`);
    } catch (error) {
      if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
    }
    const parent = path.dirname(cursor);
    if (parent === cursor) return null;
    cursor = parent;
  }
}

function runtimePackageLocator(packageRoot: string, runtimeRoot: string): string {
  if (runtimeRoot === packageRoot) return 'root:.';
  const relative = path.relative(packageRoot, runtimeRoot).split(path.sep).join('/');
  return `relative:${relative}`;
}

/** Protocols that name a checkout-local package instead of a registry tarball. */
const LOCAL_LINK_PROTOCOL_PATTERN = /^(?:file|link|workspace|portal):/;
/** npm's bare local-path shorthands: `./x`, `../x`, `/x`, `~/x`, `C:\x`. */

View on GitHub (pinned to aac7515d2a)

Solutions

  1. Rename or remove the offending path: it must be a regular file (or a symlink to one) if it keeps the name package-lock.json.
  2. Move collected lockfiles out of that directory and place the active one at the expected file path.
  3. Check ancestors too — the walk inspects every level up to the filesystem root.

Example fix

# before
package-lock.json/
  ├── a/package-lock.json
  └── b/package-lock.json

# after
package-lock.json        # regular file (the active lockfile)
locks/
  ├── a/package-lock.json
  └── b/package-lock.json
Defensive patterns

Strategy: validation

Validate before calling

import { lstatSync } from 'node:fs';
import path from 'node:path';

function lockfileCandidatesAreRegular(packageRoot: string): boolean {
  let cursor = packageRoot;
  while (true) {
    const candidate = path.join(cursor, 'package-lock.json');
    try {
      const stat = lstatSync(candidate);
      if (!stat.isFile() && !stat.isSymbolicLink()) return false;
    } catch {
      /* ENOENT is fine */
    }
    const parent = path.dirname(cursor);
    if (parent === cursor) return true;
    cursor = parent;
  }
}

Type guard

function isLockfileNotRegularFileError(error: unknown): boolean {
  return error instanceof Error && /^Analyzer package lock is not a regular file:/.test(error.message);
}

Try / catch

try {
  identity = resolveAnalyzerRunnerIdentity(import.meta.url);
} catch (error) {
  if (isLockfileNotRegularFileError(error)) {
    reportUserError(`package-lock.json exists but is not a file: ${error.message}; rename or remove it.`);
  }
  throw error;
}

Prevention

When it happens

Trigger: A directory or special file literally named package-lock.json in the package root or any ancestor directory while resolveAnalyzerRunnerIdentity locates the lockfile — e.g. someone made package-lock.json/ a folder to hold multiple lockfiles, or a tool created a lock/pid artifact with that name.

Common situations: Monorepos that store per-package lockfiles in a directory named package-lock.json/; scripts writing lock markers with unlucky names; container images copying a directory over the lockfile path.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20). Data as JSON: /api/errors/23316b332152629b. Report an issue: GitHub.