{"record":{"id":"f182bfe6012d66cb","repo":"abhigyanpatwari/GitNexus","slug":"trusted-cache-directory-env-must-name-a-pre-exi","errorCode":null,"errorMessage":"${TRUSTED_CACHE_DIRECTORY_ENV} must name a pre-existing protected non-symlink directory","messagePattern":"(.+?) must name a pre-existing protected non-symlink directory","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/analyzer-identity.ts","lineNumber":2077,"sourceCode":"  return process.platform === 'win32' ? left.toLowerCase() === right.toLowerCase() : left === right;\n}\n\nfunction trustedEnvironmentCacheDirectory(): string | null {\n  const configured = process.env[TRUSTED_CACHE_DIRECTORY_ENV];\n  if (configured === undefined) return null;\n  if (configured.length === 0 || configured.includes('\\0') || !path.isAbsolute(configured)) {\n    throw new Error(`${TRUSTED_CACHE_DIRECTORY_ENV} must name an absolute protected directory`);\n  }\n  const normalized = path.normalize(configured);\n  let resolved: string;\n  try {\n    const link = lstatSync(normalized);\n    if (!link.isDirectory() || link.isSymbolicLink()) {\n      throw new Error('not a real directory');\n    }\n    resolved = realpathSync.native(normalized);\n  } catch {\n    throw new Error(\n      `${TRUSTED_CACHE_DIRECTORY_ENV} must name a pre-existing protected non-symlink directory`,\n    );\n  }\n  // Reject junctions/symlinked ancestors as well as a symlink final component.\n  // The environment variable is an explicit trust assertion, but its spelling\n  // must still bind exactly to the directory the cache will use.\n  if (!pathsEqual(path.resolve(normalized), resolved)) {\n    throw new Error(`${TRUSTED_CACHE_DIRECTORY_ENV} must not traverse symbolic links or junctions`);\n  }\n  return resolved;\n}\n\nfunction cacheDirectory(\n  options: AnalyzerIdentityResolveOptions,\n  packageRoot: string,\n  buildRoot: string,\n): string | null {\n  // An explicit location is a trusted operator/test override and therefore","sourceCodeStart":2059,"sourceCodeEnd":2095,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus/src/core/analyzer-identity.ts#L2059-L2095","documentation":"Thrown by trustedEnvironmentCacheDirectory when the configured path is syntactically absolute (passes error 91's check) but lstat/realpath fail or show it is not a real directory. The cache directory must pre-exist as a plain directory the analyzer can write to; the analyzer will NOT create the env-var-configured location (unlike the options.cacheDirectory override, which it does mkdir).","triggerScenarios":"trustedEnvironmentCacheDirectory lstatSync(normalized) either throws (ENOENT/permission) or returns a non-directory / a symlink; realpathSync.native then throws. The catch wraps both into this single message. So: a non-existent path, a file, a symlink-to-dir, or an unreadable path all surface here.","commonSituations":"Operator pointed the env var at a directory they forgot to `mkdir -p`; pointed it at a symlink (forbidden because the trust must bind to the exact real directory); pointed it at a path on a mount not yet available at analyzer start (NFS/cifs race); the directory was removed between deploys.","solutions":["Pre-create the directory: `mkdir -p /var/cache/gitnexus-analyzer-identity && chmod 700 /var/cache/gitnexus-analyzer-identity`.","Ensure the path is a REAL directory, not a symlink: `ls -ld <path>` should show `drwx...` with no `->`.","If you need it auto-created, pass options.cacheDirectory instead of the env var — cacheDirectory() runs mkdirSync(explicit, {recursive:true, mode:0o700}).","Confirm the mount is up before analyzer start (for network filesystems)."],"exampleFix":"# before: env points at a path that was never created\n#   export GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/opt/gn-id\n#   -> \"...must name a pre-existing protected non-symlink directory\"\n#\n# after: pre-create with restrictive perms\n#   sudo mkdir -p /opt/gn-id && sudo chmod 700 /opt/gn-id && sudo chown $USER /opt/gn-id","handlingStrategy":"validation","validationCode":"const fs = require('node:fs');\nconst path = require('node:path');\nfunction validateTrustedCacheExists() {\n  const v = process.env.GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR;\n  if (!v) return;\n  const st = fs.lstatSync(v); // throws if missing -> caller must mkdir first\n  if (!st.isDirectory()) throw new Error(`${v} is not a directory`);\n  if (st.isSymbolicLink()) throw new Error(`${v} is a symlink; use the real path`);\n}\n// Pre-create before validating:\n//   const v = process.env.GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR;\n//   if (v) fs.mkdirSync(v, { recursive: true, mode: 0o700 });","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pre-create the cache dir before analyzer start: `mkdir -p <dir> && chmod 700 <dir>`.","Use the real path, never a symlink — lstat must report a plain directory.","If you want auto-creation, pass options.cacheDirectory instead of the env var.","On network filesystems, confirm the mount is up before start."],"tags":["analyzer-identity","cache","configuration","environment","filesystem"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}