abhigyanpatwari/GitNexus · error · Error
${TRUSTED_CACHE_DIRECTORY_ENV} must not traverse symbolic li
Error message
${TRUSTED_CACHE_DIRECTORY_ENV} must not traverse symbolic links or junctions What it means
Thrown by trustedEnvironmentCacheDirectory when the directory exists and is a real directory, BUT path.resolve(normalized) differs from realpathSync.native(normalized) — meaning some component of the path (an ancestor or the final segment) is a symlink/junction. Even though lstat already rejected a symlink final segment, this second check catches junctions and symlinked ancestors, because the trust assertion must bind to the exact on-disk directory.
Source
Thrown at gitnexus/src/core/analyzer-identity.ts:2085
}
const normalized = path.normalize(configured);
let resolved: string;
try {
const link = lstatSync(normalized);
if (!link.isDirectory() || link.isSymbolicLink()) {
throw new Error('not a real directory');
}
resolved = realpathSync.native(normalized);
} catch {
throw new Error(
`${TRUSTED_CACHE_DIRECTORY_ENV} must name a pre-existing protected non-symlink directory`,
);
}
// Reject junctions/symlinked ancestors as well as a symlink final component.
// The environment variable is an explicit trust assertion, but its spelling
// must still bind exactly to the directory the cache will use.
if (!pathsEqual(path.resolve(normalized), resolved)) {
throw new Error(`${TRUSTED_CACHE_DIRECTORY_ENV} must not traverse symbolic links or junctions`);
}
return resolved;
}
function cacheDirectory(
options: AnalyzerIdentityResolveOptions,
packageRoot: string,
buildRoot: string,
): 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 });View on GitHub (pinned to d540b00184)
Solutions
- Point at the realpath directly: run `realpath <configured>` and set the env var to that resolved path.
- On macOS prefer `/private/tmp/...` or `$HOME/...` over `/tmp/...`.
- On Windows replace junctions with real directories on the target drive.
- Re-check after fixing: path.resolve and realpath must agree — verify by comparing the output of `realpath <dir>` against the env var value.
Example fix
# before: /tmp is a symlink to /private/tmp on macOS # export GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/tmp/gn-id # -> "...must not traverse symbolic links or junctions" # # after: use the real path # export GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/private/tmp/gn-id
Defensive patterns
Strategy: validation
Validate before calling
const fs = require('node:fs');
const path = require('node:path');
function validateTrustedCacheNoSymlink() {
const v = process.env.GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR;
if (!v) return;
const normalized = path.normalize(v);
const resolved = fs.realpathSync.native(normalized);
const same = process.platform === 'win32'
? path.resolve(normalized).toLowerCase() === resolved.toLowerCase()
: path.resolve(normalized) === resolved;
if (!same) {
throw new Error(`${v} resolves to ${resolved}; set the env var to the real path.`);
}
}
// validateTrustedCacheNoSymlink(); Prevention
- Run `realpath <dir>` and use its output as the env value.
- On macOS avoid `/tmp` and `/var` (they symlink to `/private/...`); use `/private/tmp` or `$HOME`.
- On Windows avoid junctions; use real directories on the target drive.
- Verify equality: `[[ "$(realpath "$V")" == "$V" ]]`.
When it happens
Trigger: The configured absolute path resolves to a different real path than its lexical form: an ancestor directory is a symlink (e.g. `/tmp` -> `/private/tmp` on macOS, or a `/var` symlink common on some Linux distros), or the path is a junction on Windows. pathsEqual(resolve(normalized), resolved) returns false and the throw fires.
Common situations: macOS `/tmp` (which symlinks to `/private/tmp`) — using `/tmp/gn-id` trips this because `/tmp` is a symlink; `/var` -> `/private/var` similarly; Windows junctions used to redirect a folder onto another drive; a docker volume mount that presents as a symlinked ancestor.
Related errors
- ${TRUSTED_CACHE_DIRECTORY_ENV} must name an absolute protect
- ${TRUSTED_CACHE_DIRECTORY_ENV} must name a pre-existing prot
- ${TRUSTED_CACHE_DIRECTORY_ENV} must be outside the analyzer
- Analyzer runtime payload scan exceeded ${limits.runtimePaylo
- GitNexus package version is unavailable in ${packageRoot}
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/770c4166f1231143.
Report an issue: GitHub.