abhigyanpatwari/GitNexus · error · Error
Analyzer build changed while its identity was being computed
Error message
Analyzer build changed while its identity was being computed: ${buildRoot} What it means
Thrown by resolveAnalyzerRunnerIdentity when the build tree's entry snapshot changed between the pre-hash collection (buildSnapshot of the first collectBuildEntries) and a fresh post-hash re-collection. This is a TOCTOU guard: if the build output mutated while the identity was being computed, the digest would not describe a stable artifact, so the resolution aborts rather than persist a misleading receipt.
Source
Thrown at gitnexus/src/core/analyzer-identity.ts:2736
const dependencyInputs = collectDependencyInputs(packageRoot, options, traversalLimits);
const dependencySnapshotBefore = dependencySnapshot(dependencyInputs);
const dependency = hashDependencyRuntime(
dependencyInputs,
previousCache,
options,
runtimeVariant,
);
const packageVersion = dependencyInputs.packages[0]?.manifest.version;
if (typeof packageVersion !== 'string' || packageVersion.trim() === '') {
throw new Error(`GitNexus package version is unavailable in ${packageRoot}`);
}
const buildSnapshotAfter = buildSnapshot(
collectBuildEntries(buildRoot, options, traversalLimits),
);
if (!isDeepStrictEqual(build.snapshot, buildSnapshotAfter)) {
throw new Error(`Analyzer build changed while its identity was being computed: ${buildRoot}`);
}
const dependencySnapshotAfter = dependencySnapshot(
collectDependencyInputs(packageRoot, options, traversalLimits),
);
if (!isDeepStrictEqual(dependencySnapshotBefore, dependencySnapshotAfter)) {
throw new Error(
`Analyzer dependency runtime changed while its identity was being computed: ${packageRoot}`,
);
}
const nextCache: IdentityCachePayload = {
schemaVersion: IDENTITY_CACHE_SCHEMA_VERSION,
packageRoot,
buildRoot,
packageVersion,
buildKind: kind,
buildCanonicalization: BUILD_CANONICALIZATION,
dependencyCanonicalization: DEPENDENCY_RUNTIME_CANONICALIZATION,View on GitHub (pinned to d540b00184)
Solutions
- Quiesce the build tree: stop `tsc --watch`, bundlers, test runners, and any process writing to buildRoot, then retry.
- Run the analyzer from a freshly built, frozen artifact: `tsc && node .gitnexus/run.cjs analyze --index-only` in one shot without watch mode.
- If using a CI, snapshot the build output into a read-only location and point the analyzer there.
- Disable incremental-build file watchers on the analyzer process.
Example fix
// before: analyzer runs concurrently with `tsc --watch` mutating dist/ // -> "Analyzer build changed while its identity was being computed: /repo/dist" // // after: build once, stop watchers, then analyze // $ pkill -f 'tsc --watch' // $ tsc // $ node .gitnexus/run.cjs analyze --index-only
Defensive patterns
Strategy: retry
Validate before calling
const fs = require('node:fs');
function isBuildTreeQuiescent(buildRoot) {
// Sample mtimes twice with a small gap; if any changed, the tree is being written to.
const snap = () => {
const out = {};
const walk = (d) => {
for (const e of fs.readdirSync(d, { withFileTypes: true })) {
const p = require('node:path').join(d, e.name);
out[p] = fs.statSync(p).mtimeMs;
if (e.isDirectory()) walk(p);
}
};
walk(buildRoot);
return out;
};
const a = snap();
require('node:fs').setTimeoutSync?.; // no-op; use Atomics.wait below
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 250);
const b = snap();
for (const k of Object.keys(b)) if (a[k] !== b[k]) return false;
return true;
}
// if (!isBuildTreeQuiescent(buildRoot)) throw new Error('build tree is being mutated; stop writers before analyze'); Try / catch
try {
identity = resolveAnalyzerRunnerIdentity(analyzerModuleUrl, options);
} catch (err) {
if (err.message.startsWith('Analyzer build changed while its identity was being computed:')) {
// TOCTOU race with a concurrent writer; a single retry on a quiescent tree usually succeeds.
identity = resolveAnalyzerRunnerIdentity(analyzerModuleUrl, options);
} else throw err;
} Prevention
- Stop `tsc --watch`, bundlers, and test runners that write into the build directory before identity resolution.
- Run `tsc` (one-shot) then analyze; do not analyze while a watcher is active.
- In CI, snapshot the build output into a read-only location and analyze from there.
- Treat a repeat failure as a genuine concurrency defect in your build pipeline, not transient.
When it happens
Trigger: buildSnapshot(collectBuildEntries(buildRoot,...)) is computed twice — once before hashing build artifacts and once after dependency collection; isDeepStrictEqual(build.snapshot, buildSnapshotAfter) compares them. Any difference in build entries (added/removed/changed files, reordered guards) between the two passes throws, naming buildRoot.
Common situations: A concurrent `tsc --watch`, bundler, or test runner writing into the build directory during identity resolution; a build that writes incrementally and was still flushing when the analyzer started; a deploy copying fresh files into buildRoot mid-scan; an IDE indexer touching file mtimes; a container overlayfs applying deferred writes.
Related errors
- Analyzer dependency runtime changed while its identity was b
- Analyzer build or dependency runtime changed while its ident
- Analyzer runtime payload directory is unavailable: ${absolut
- Invoked analyzer artifact is absent from the validated build
- Analyzer build or dependency runtime changed during analysis
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/19c3d43d995f4775.
Report an issue: GitHub.