abhigyanpatwari/GitNexus · error
Analyzer vendored runtime directory is unavailable
Error message
Analyzer vendored runtime directory is unavailable: ${vendorRoot} What it means
collectVendoredGrammarInputs first checks the vendor/ directory exists, then calls recordDirectoryGuard(vendorRoot) to register a tamper guard on it; if that registration returns false the vendored runtime directory is considered unavailable/unstable and the error is thrown. It protects against the vendor directory appearing then disappearing or being unstatable between the two checks (TOCTOU), which would otherwise let an unguarded directory mutate after identity computation.
Solutions
- Verify the directory is readable and stable: ls -la <packageRoot>/vendor run twice; fix permissions if the second stat fails (chmod/chown on the package root).
- Stop concurrent installs/sync tools touching the tree and re-run analyze — the guard is racing a mutation.
- Move the GitNexus install off network/ephemeral storage onto a local filesystem.
- If vendor/ is not actually needed (optional grammars), reinstall the package so the tree is consistent rather than half-materialized.
Example fix
# before $ npx gitnexus analyze # Analyzer vendored runtime directory is unavailable: /usr/lib/node_modules/gitnexus/vendor # after: fix permissions and stop the racing sync client sudo chown -R "$USER" /usr/lib/node_modules/gitnexus killall Dropbox 2>/dev/null || true npx gitnexus analyze
Defensive patterns
Strategy: retry
Validate before calling
// Verify the vendored grammar directory is stably readable before analyze:
import { lstatSync } from 'node:fs';
function vendorRootStable(packageRoot: string): boolean {
const vendorRoot = join(packageRoot, 'vendor');
for (let i = 0; i < 3; i++) {
try {
const s = lstatSync(vendorRoot);
if (!s.isDirectory()) return false;
} catch {
return false;
}
}
return true;
} Try / catch
try {
await resolveAnalyzerRunnerIdentity(moduleUrl);
} catch (err) {
if (err instanceof Error && err.message.includes('vendored runtime directory is unavailable')) {
await stopSyncToolsAndConcurrentInstalls(); // then retry once
return resolveAnalyzerRunnerIdentity(moduleUrl);
}
throw err;
} Prevention
- Install gitnexus on a local filesystem, not NFS/sync-drive mounts.
- Pause antivirus/sync clients during analysis of the install tree.
- Never run two npm installs against the same tree concurrently.
- Check directory permissions after Docker COPY operations that can clobber modes.
When it happens
Trigger: resolveAnalyzerRunnerIdentity() on a package whose <packageRoot>/vendor exists and is a directory, but recordDirectoryGuard(vendorRoot) fails — typically because lstat/stat on the directory errors (permission denied, disappearing mid-scan, NFS/ephemeral mounts) after existsSync already passed.
Common situations: vendor/ mounted on a flaky network filesystem, an antivirus or sync client (Dropbox/OneDrive) locking the directory, a concurrent npm install mutating the tree, or permission changes on the directory between the two syscalls.
Related errors
- Analyzer build changed while its identity was being computed
- Analyzer identity directory changed while it was read
- Analyzer build or dependency runtime changed during…
- Analyzer build or dependency runtime changed while its…
- Analyzer dependency runtime changed while its identity was…
AI-assisted analysis of abhigyanpatwari/GitNexus@52924ef12c (2026-08-20).
Data as JSON: /api/errors/494b1e226613111e.
Report an issue: GitHub.
Appendix: source
Thrown at gitnexus/src/core/analyzer-identity.ts:1440
packageRoot: string,
directoryGuards: Map<string, DependencyDirectoryGuard>,
options: AnalyzerIdentityResolveOptions,
budget: RuntimeArtifactScanBudget,
limits: AnalyzerIdentityTraversalLimits,
): {
manifests: DependencyInputs['vendoredManifests'];
artifacts: RuntimeArtifact[];
} {
const vendorRoot = path.join(packageRoot, 'vendor');
if (!existsSync(vendorRoot) || !lstatSync(vendorRoot).isDirectory()) {
recordDirectoryGuard(directoryGuards, packageRoot);
return { manifests: [], artifacts: [] };
}
const manifests: DependencyInputs['vendoredManifests'] = [];
const artifacts: RuntimeArtifact[] = [];
if (!recordDirectoryGuard(directoryGuards, vendorRoot)) {
throw new Error(`Analyzer vendored runtime directory is unavailable: ${vendorRoot}`);
}
const vendorEntries = readDirectory(vendorRoot, options);
budget.entries += vendorEntries.length;
if (budget.entries > limits.runtimeEntries) {
throw new Error(
`Analyzer runtime payload scan exceeded ${limits.runtimeEntries} entries: ${vendorRoot}`,
);
}
for (const entry of vendorEntries) {
if (!entry.isDirectory() || !entry.name.startsWith('tree-sitter-')) continue;
const grammarRoot = path.join(vendorRoot, entry.name);
const manifestPath = path.join(grammarRoot, 'package.json');
if (isFile(manifestPath)) {
options.onCacheMissWork?.({ kind: 'manifest-read', path: manifestPath });
const read = readStableFileWithinBudget(manifestPath, budget, limits.runtimeBytes);
manifests.push({
canonicalPath: `vendor:${entry.name}/package.json`,
absolutePath: manifestPath,View on GitHub (pinned to 52924ef12c)