abhigyanpatwari/GitNexus · error · Error
Analyzer identity input changed while it was being read: ${c
Error message
Analyzer identity input changed while it was being read: ${candidate} What it means
resolveAnalyzerRunnerIdentity() double-reads every identity input (manifests, lockfile) and compares full stat snapshots (dev/ino/size/mtimeNs/ctimeNs) taken before and after the read; if they differ on both of two attempts, the file is considered concurrently mutated and the resolve aborts. This is a deliberate TOCTOU guard: stamping an identity from a file that changed mid-read would make the receipt meaningless. The error names the exact file that refused to stabilize.
Source
Thrown at gitnexus/src/core/analyzer-identity.ts:527
entriesDigest: string;
} {
for (let attempt = 0; attempt < 2; attempt += 1) {
const before = snapshotDirectory(candidate);
const entriesDigest = directoryEntriesDigest(candidate);
const after = snapshotDirectory(candidate);
if (isDeepStrictEqual(before, after)) return { state: after, entriesDigest };
}
throw new Error(`Analyzer identity directory changed while it was read: ${candidate}`);
}
function readStableFile(candidate: string): { bytes: Buffer; state: ReadableFileState } {
for (let attempt = 0; attempt < 2; attempt += 1) {
const before = snapshotReadableFile(candidate);
const bytes = readFileSync(candidate);
const after = snapshotReadableFile(candidate);
if (isDeepStrictEqual(before, after)) return { bytes, state: after };
}
throw new Error(`Analyzer identity input changed while it was being read: ${candidate}`);
}
function readStableFileWithinBudget(
candidate: string,
budget: RuntimeArtifactScanBudget,
maxBytes: number,
): { bytes: Buffer; state: ReadableFileState } {
const before = snapshotReadableFile(candidate);
const bytes = stateSize(before.target, candidate);
if (budget.bytes + bytes > maxBytes) {
throw new Error(`Analyzer runtime scan exceeded ${maxBytes} bytes: ${candidate}`);
}
const stable = readStableFile(candidate);
budget.bytes += stable.bytes.length;
return stable;
}
/** Hash a stable file through a fixed-size buffer instead of materializing it. */View on GitHub (pinned to aac7515d2a)
Solutions
- Re-run the analyze/status command once the concurrent package-manager or watcher process has finished — the error is transient by design.
- Serialize the steps: complete 'npm ci'/'npm install' (and any build that rewrites dist/) before invoking gitnexus.
- Stop or pause file watchers (tsx watch, nodemon, dev servers) and antivirus/indexing of the gitnexus install directory during the run.
- If it persists on an idle filesystem, verify the disk/fs is not corrupt (stat metadata flapping) with 'stat <file>' twice and comparing.
- Programmatic callers can wrap resolveAnalyzerRunnerIdentity in a bounded retry (see exampleFix).
Example fix
// before
const identity = resolveAnalyzerRunnerIdentity(import.meta.url);
// after (bounded retry for the transient TOCTOU window)
function resolveStable(url: string, attempts = 3): ReturnType<typeof resolveAnalyzerRunnerIdentity> {
for (let i = 0; ; i += 1) {
try {
return resolveAnalyzerRunnerIdentity(url);
} catch (error) {
if (i === attempts - 1 || !/^Analyzer identity (directory|input) changed while/.test(String((error as Error).message))) {
throw error;
}
}
}
} Defensive patterns
Strategy: retry
Type guard
function isIdentityInputChangedError(error: unknown): boolean {
return error instanceof Error && /^Analyzer identity input changed while it was being read:/.test(error.message);
} Try / catch
try {
identity = resolveAnalyzerRunnerIdentity(import.meta.url);
} catch (error) {
if (!isIdentityInputChangedError(error)) throw error;
await sleep(250);
identity = resolveAnalyzerRunnerIdentity(import.meta.url); // one bounded retry
} Prevention
- Never run package installs/upgrades concurrently with gitnexus analyze or status.
- Exclude the gitnexus install directory from file watchers and antivirus real-time scanning.
- In CI, make the install step and the analyze step sequential, not parallel.
When it happens
Trigger: readStableFile() is reached via resolveAnalyzerRunnerIdentity -> collectDependencyInputs -> readManifest/findNearestPackageLock while hashing a package.json/package-lock.json whose stat state changes between snapshotReadableFile() calls twice in a row. Typical producers: npm/pnpm/yarn install or uninstall running concurrently with 'gitnexus analyze' or 'gitnexus status', a file watcher (tsx watch, dev server, antivirus) rewriting the file, or a package manager hard-linking/retargeting files during the scan.
Common situations: CI pipelines that run 'npm install' and 'gitnexus analyze' in parallel or back-to-back without waiting; Docker builds where layer materialization touches mtimes during the run; Windows Defender or indexer touching ctime/mtime; a second terminal running an install while a server analyze-worker resolves identity.
Related errors
- Analyzer identity input changed while it was being hashed: $
- Analyzer runtime payload directory is unavailable: ${absolut
- Analyzer runtime scan exceeded ${maxBytes} bytes: ${candidat
- Unsupported analyzer build entry: ${absolutePath}
- Analyzer package lock symbolic link does not resolve to a fi
AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20).
Data as JSON: /api/errors/81ca825e683f5134.
Report an issue: GitHub.