abhigyanpatwari/GitNexus · error
must be outside the analyzer package and build roots
Error message
${TRUSTED_CACHE_DIRECTORY_ENV} must be outside the analyzer package and build roots What it means
Even a valid, real, absolute GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR is rejected if it lies inside the analyzer package root or build root. A cache nested inside the trees being hashed would create a feedback loop: persisting a cache entry changes the hashed inputs, invalidating the identity it just computed and defeating the tamper guards.
Solutions
- Move the cache outside the installation: export GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/var/cache/gitnexus-identity (create it first).
- In CI, cache an external directory and mount/restore it at a path outside the package and build roots.
- If you do not need a custom location, unset the variable and use the default cache directory.
- Note options.cacheDirectory (programmatic) is exempt — only the env-var path enforces this rule.
Example fix
# before export GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/usr/lib/node_modules/gitnexus/.id-cache # ...must be outside the analyzer package and build roots # after sudo mkdir -p /var/cache/gitnexus-identity && sudo chown "$USER" /var/cache/gitnexus-identity export GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/var/cache/gitnexus-identity
Defensive patterns
Strategy: validation
Validate before calling
// Ensure the configured cache dir is outside the install tree:
import { relative, isAbsolute } from 'node:path';
function cacheDirOutsideRoots(cacheDir: string, packageRoot: string, buildRoot: string): boolean {
const outside = (root: string) => {
const rel = relative(resolve(root), resolve(cacheDir));
return rel === '' || rel.startsWith('..') || isAbsolute(rel) ? !(rel === '') : false;
};
return outside(packageRoot) && outside(buildRoot);
} Try / catch
try {
spawnSync('gitnexus', ['analyze']);
} catch (err) {
if (String((err as Error).message).includes('must be outside the analyzer package and build roots')) {
delete process.env.GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR; // fall back to the default cache
return spawnSync('gitnexus', ['analyze']);
}
throw err;
} Prevention
- Choose OS-level cache locations (/var/cache, $HOME/.cache) for the identity cache, never inside node_modules.
- In CI, restore cached identity data to paths outside the checkout/install.
- Remember only the env var is constrained; programmatic options.cacheDirectory is the deliberate override route.
- Audit Dockerfiles that COPY cached state into the package directory.
When it happens
Trigger: cacheDirectory() computing isInside(packageRoot, configured) || isInside(buildRoot, configured) as true — e.g. the env var set to <packageRoot>/.identity-cache or <packageRoot>/node_modules/... while resolveAnalyzerRunnerIdentity() runs against that same package.
Common situations: Users tucking the cache 'neatly' inside the install dir, Docker images copying a cached directory into the package, or CI caching configurations that write into node_modules/gitnexus.
Related errors
- must name a pre-existing protected non-symlink directory
- must name an absolute protected directory
- must not traverse symbolic links or junctions
- Analyzer build changed while its identity was being computed
- Analyzer build or dependency runtime changed during…
AI-assisted analysis of abhigyanpatwari/GitNexus@52924ef12c (2026-08-20).
Data as JSON: /api/errors/35d8499891aaeabc.
Report an issue: GitHub.
Appendix: source
Thrown at gitnexus/src/core/analyzer-identity.ts:2114
): string | null {
// An explicit location is a trusted operator/test override and therefore
// remains authoritative, including when the secure default is unavailable.
if (options.cacheDirectory) {
const explicit = path.resolve(options.cacheDirectory);
try {
// Create it before any build/dependency directory guards are captured.
// A cache nested immediately under a package root then changes that
// parent's directory state once, not after we persist the first entry.
mkdirSync(explicit, { recursive: true, mode: 0o700 });
} catch {
/* persistence remains optional and will fail closed */
}
return explicit;
}
const configured = trustedEnvironmentCacheDirectory();
if (configured) {
if (isInside(packageRoot, configured) || isInside(buildRoot, configured)) {
throw new Error(
`${TRUSTED_CACHE_DIRECTORY_ENV} must be outside the analyzer package and build roots`,
);
}
return configured;
}
return defaultCacheDirectory();
}
function hasTrustedCacheOverride(options: AnalyzerIdentityResolveOptions): boolean {
return (
options.cacheDirectory !== undefined || process.env[TRUSTED_CACHE_DIRECTORY_ENV] !== undefined
);
}
function identityCacheKey(
packageRoot: string,
buildRoot: string,
runtimeVariant: RuntimeVariant,View on GitHub (pinned to 52924ef12c)