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
- 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.
- Move collected lockfiles out of that directory and place the active one at the expected file path.
- 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
- Never store multiple lockfiles inside a directory named package-lock.json/.
- Audit ancestor directories for stray artifacts with that exact name when provisioning environments.
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
- Unsupported analyzer build entry: ${absolutePath}
- Analyzer package lock symbolic link does not resolve to a fi
- Analyzer identity input changed while it was being read: ${c
- Analyzer runtime scan exceeded ${maxBytes} bytes: ${candidat
- Analyzer identity input changed while it was being hashed: $
AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20).
Data as JSON: /api/errors/23316b332152629b.
Report an issue: GitHub.