{"record":{"id":"66e33c3ec6b52a48","repo":"ruvnet/ruflo","slug":"repository-changed-while-hashing-untracked-entry","errorCode":null,"errorMessage":"repository changed while hashing untracked entry: ${path}","messagePattern":"repository changed while hashing untracked entry: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/codex/src/harness/repository-state.ts","lineNumber":274,"sourceCode":"    let kind: UntrackedFileIdentity['kind'];\n    let content: Buffer;\n    if (before.isSymbolicLink()) {\n      kind = 'symlink';\n      content = Buffer.from(readlinkSync(absolute), 'utf8');\n    } else if (before.isFile()) {\n      kind = 'file';\n      content = readFileSync(absolute);\n    } else {\n      throw new Error(`unsupported untracked repository entry: ${path}`);\n    }\n    const after = lstatSync(absolute);\n    if (\n      before.mode !== after.mode\n      || before.size !== after.size\n      || before.mtimeMs !== after.mtimeMs\n      || before.ino !== after.ino\n    ) {\n      throw new Error(`repository changed while hashing untracked entry: ${path}`);\n    }\n    return {\n      path,\n      kind,\n      mode: before.mode & 0o7777,\n      bytes: content.byteLength,\n      digest: digest(content),\n    };\n  });\n  return { digest: digest(canonicalJson(entries)), entries };\n}\n\nfunction trackedPatch(repoRoot: string): ContentDigest {\n  return contentDigest(gitBuffer(repoRoot, [\n    'diff',\n    '--binary',\n    '--full-index',\n    '--no-ext-diff',","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/codex/src/harness/repository-state.ts#L256-L292","documentation":"untrackedManifest() lstat's each untracked entry before and after reading its content and compares mode, size, mtimeMs, and ino. Any difference means the file changed between the two stats (a TOCTOU window), so the capture refuses to record a torn state instead of hashing a half-written file.","triggerScenarios":"Any process writes to an untracked file during capture: build output, `npm install`, formatters, watch mode, another agent or editor saving the file mid-read.","commonSituations":"Running capture while `npm run build`, a bundler --watch, or another AI agent is actively writing to the worktree; CI pipelines that generate artifacts during the same phase as the snapshot.","solutions":["Stop the concurrent writer (watchers, builds, other agent sessions) and re-run the capture","Commit or stash work so the tree is quiescent before capturing","Retry the capture — transient writes usually clear on the second attempt"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"function isQuiescent(abs: string): boolean {\n  const a = lstatSync(abs);\n  const content = readFileSync(abs);\n  const b = lstatSync(abs);\n  return a.mtimeMs === b.mtimeMs && a.size === b.size && a.ino === b.ino;\n}","typeGuard":null,"tryCatchPattern":"for (let attempt = 1; attempt <= 3; attempt++) {\n  try { return capture(); }\n  catch (e) { if (!/changed while hashing/.test(String(e))) throw e; }\n}","preventionTips":["Stop watchers, builds, and other agent sessions before capturing","Prefer clean checkouts for snapshotting instead of live workspaces","Stash or commit dirty state so the clean fast path applies"],"tags":["race-condition","filesystem","git","harness"],"backgroundTag":"concurrent-modification-detected","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}