{"record":{"id":"f5ea3c49d16f1545","repo":"abhigyanpatwari/GitNexus","slug":"analyzer-identity-directory-changed-while-it-was-r","errorCode":null,"errorMessage":"Analyzer identity directory changed while it was read: ${candidate}","messagePattern":"Analyzer identity directory changed while it was read: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/analyzer-identity.ts","lineNumber":517,"sourceCode":"  for (const entry of entries) updateCanonicalFrame(hash, [entry.name, entry.kind]);\n  return `sha256:${hash.digest('hex')}`;\n}\n\nfunction directoryEntriesDigest(candidate: string): string {\n  return directoryEntriesDigestFrom(readdirSync(candidate, { withFileTypes: true }));\n}\n\nfunction snapshotDirectoryInventory(candidate: string): {\n  state: StatState;\n  entriesDigest: string;\n} {\n  for (let attempt = 0; attempt < 2; attempt += 1) {\n    const before = snapshotDirectory(candidate);\n    const entriesDigest = directoryEntriesDigest(candidate);\n    const after = snapshotDirectory(candidate);\n    if (isDeepStrictEqual(before, after)) return { state: after, entriesDigest };\n  }\n  throw new Error(`Analyzer identity directory changed while it was read: ${candidate}`);\n}\n\nfunction readStableFile(candidate: string): { bytes: Buffer; state: ReadableFileState } {\n  for (let attempt = 0; attempt < 2; attempt += 1) {\n    const before = snapshotReadableFile(candidate);\n    const bytes = readFileSync(candidate);\n    const after = snapshotReadableFile(candidate);\n    if (isDeepStrictEqual(before, after)) return { bytes, state: after };\n  }\n  throw new Error(`Analyzer identity input changed while it was being read: ${candidate}`);\n}\n\nfunction readStableFileWithinBudget(\n  candidate: string,\n  budget: RuntimeArtifactScanBudget,\n  maxBytes: number,\n): { bytes: Buffer; state: ReadableFileState } {\n  const before = snapshotReadableFile(candidate);","sourceCodeStart":499,"sourceCodeEnd":535,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/core/analyzer-identity.ts#L499-L535","documentation":"Analyzer identity fingerprinting double-snapshots each directory (stat state before and after listing entries) and retries once internally; if both attempts observe the directory changing mid-read, it gives up and throws. It is a TOCTOU guard: the identity digest must be computed from a stable view of the build/dependency directories, so concurrent mutation makes the result untrustworthy.","triggerScenarios":"Running analyze while a build, npm/yarn/pnpm install, code generator, formatter-on-save, or another gitnexus process is writing into the directories being fingerprinted (the analyzer build root and dependency directories); hot rebuild loops with tight write cadence can defeat both attempts.","commonSituations":"CI running analyze in parallel with an install step or a bundler watch; local dev with a file watcher regenerating outputs continuously; two analyze runs racing on the same checkout; Docker bind mounts with slow metadata sync flipping mtimes during the snapshot.","solutions":["Simply re-run analyze — the error is transient and single-shot interference almost never repeats","Serialize the work: finish installs/builds (or cancel watchers) before invoking analyze; don't run two analyze processes on the same tree","For Docker bind mounts, prefer named volumes or run analyze before mounting, to avoid mtime churn from host sync","If it recurs, identify the writer: lsof +D <path> or check what regenerates the directory during the run"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for (let attempt = 1; attempt <= 3; attempt += 1) {\n  try {\n    return await computeAnalyzerIdentity(request);\n  } catch (err) {\n    const msg = (err as Error).message ?? '';\n    const transient = msg.includes('changed while it was read');\n    if (!transient || attempt === 3) throw err;\n    await delay(250 * attempt); // let the concurrent writer finish\n  }\n}","preventionTips":["Run analyze after installs/builds complete, not concurrently with them","Never run two analyze processes against the same checkout at once","Pause file watchers / hot-rebuild loops during indexing","Treat this error as environmental: if it recurs, find the process mutating the fingerprinted directories (lsof +D <path>)"],"tags":["filesystem","race-condition","toctu","analyzer-identity"],"backgroundTag":"filesystem-race-condition","analyzedSha":"aac7515d2a8c50a1f8f923c6fb77218b333560d6","analyzedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}