{"record":{"id":"01c0e767b547c8b5","repo":"ruvnet/ruflo","slug":"duplicate-repository-path-path","errorCode":null,"errorMessage":"duplicate repository path: ${path}","messagePattern":"duplicate repository path: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/codex/src/harness/repository-state.ts","lineNumber":216,"sourceCode":"  assertUnicodeScalarString(path);\n  if (path.includes('\\\\')) throw new Error(`ambiguous repository path separator: ${path}`);\n  const normalized = path.normalize('NFC');\n  if (\n    normalized.length === 0\n    || isAbsolute(normalized)\n    || normalized.startsWith('-')\n    || normalized.split('/').some((part) => part === '' || part === '.' || part === '..')\n  ) {\n    throw new Error(`unsafe repository-relative path: ${path}`);\n  }\n  return normalized;\n}\n\nfunction assertNoPathCollisions(paths: readonly string[]): void {\n  const exact = new Set<string>();\n  const folded = new Map<string, string>();\n  for (const path of paths) {\n    if (exact.has(path)) throw new Error(`duplicate repository path: ${path}`);\n    exact.add(path);\n    const key = portableCaseFold(path);\n    const prior = folded.get(key);\n    if (prior !== undefined && prior !== path) {\n      throw new Error(`case-fold repository path collision: ${prior} and ${path}`);\n    }\n    folded.set(key, path);\n  }\n}\n\nfunction repositoryPaths(repoRoot: string, includeUntracked: boolean): readonly string[] {\n  const tracked = splitNul(gitBuffer(repoRoot, ['ls-files', '-z'])).map(decodeGitPath);\n  const untracked = includeUntracked\n    ? splitNul(gitBuffer(repoRoot, ['ls-files', '--others', '--exclude-standard', '-z'])).map(decodeGitPath)\n    : [];\n  const paths = [...tracked, ...untracked].sort(codeUnitCompare);\n  assertNoPathCollisions(paths);\n  return paths;","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/codex/src/harness/repository-state.ts#L198-L234","documentation":"assertNoPathCollisions() builds the sorted list of tracked plus untracked paths from `git ls-files` and refuses the capture when the exact same path string appears twice. Git's index should never emit a duplicate, so this error almost always indicates a corrupted index or a modified/wrapped git that returns repeated entries.","triggerScenarios":"`git ls-files -z` (or `--others`) emitting the same entry twice: broken .git/index after a crash, third-party git wrappers that concatenate outputs, or manually constructed path lists passed with the same entry twice.","commonSituations":"A crashed git process left a duplicated index entry; a git alias/wrapper merges results of multiple invocations; case-insensitive filesystems (macOS/Windows) surfaced two index rows that decode to the identical string.","solutions":["Run `git ls-files | sort | uniq -d` to identify the duplicated path","Rebuild the index: `rm .git/index && git reset` (or `git read-tree HEAD`)","Re-clone the repository if the index keeps corrupting, and remove any git output wrappers/aliases"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const dupes = paths.filter((p, i) => paths.indexOf(p) !== i);\nif (dupes.length) throw new Error(`duplicate paths: ${dupes.join(', ')}`);","typeGuard":null,"tryCatchPattern":"try { capture(); } catch (e) { if (/duplicate repository path/.test(String(e))) { rebuildIndex(); } throw e; }","preventionTips":["Never build path lists by concatenating outputs of multiple git commands without deduping","Use new Set(paths) before passing collections to harness code","Rebuild the index after git crashes to prevent duplicate entries"],"tags":["git","duplicate","index-corruption","validation"],"backgroundTag":"duplicate-path-collision","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}