{"record":{"id":"f5eac9e67dcb12bb","repo":"ruvnet/ruflo","slug":"ambiguous-repository-path-separator-path","errorCode":null,"errorMessage":"ambiguous repository path separator: ${path}","messagePattern":"ambiguous repository path separator: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/codex/src/harness/repository-state.ts","lineNumber":199,"sourceCode":"  if (start !== output.length) throw new Error('Git returned a non-NUL-terminated path list');\n  return records;\n}\n\nfunction decodeGitPath(bytes: Buffer): string {\n  const path = bytes.toString('utf8');\n  if (!Buffer.from(path, 'utf8').equals(bytes)) {\n    throw new Error('Git path is not valid round-trip UTF-8');\n  }\n  assertUnicodeScalarString(path);\n  if (path !== path.normalize('NFC')) {\n    throw new Error(`Git path is not NFC-normalized: ${path}`);\n  }\n  return normalizeRelativePath(path);\n}\n\nfunction normalizeRelativePath(path: string): string {\n  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);","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/codex/src/harness/repository-state.ts#L181-L217","documentation":"normalizeRelativePath rejects any repository-relative path containing a backslash. On POSIX, a backslash can be a literal filename character; on Windows, it is a directory separator — so such paths are ambiguous cross-platform and are refused before entering the source-state digest. The offending path is embedded in the message.","triggerScenarios":"A tracked file literally named with a backslash (e.g. a name created via shell redirection or a Windows-origin script writing 'dir\\file.txt' as a single filename); tools emitting Windows-style relative paths; archives extracted with translated separators producing backslash names.","commonSituations":"CI steps or generators from Windows environments using backslash joins; files created by `echo x > 'a\\b'` on Linux; vendored archives whose entries use backslashes.","solutions":["git mv the offending file to a name with the backslash replaced (hyphen, underscore, or restructured with '/': 'dir/file.txt')","Fix the generating tool to use path.posix.join for repository-relative names","Add a CI scan of git ls-files output for the backslash character to catch regressions"],"exampleFix":"// before: Windows-style join leaks into a tracked filename\nconst target = `reports\\\\${name}.txt`; // literal backslash on POSIX\n\n// after: POSIX separators for repository-relative paths\nconst target = path.posix.join('reports', `${name}.txt`);","handlingStrategy":"try-catch","validationCode":"function findBackslashPaths(paths: readonly string[]): string[] {\n  return paths.filter((p) => p.includes('\\\\'));\n}","typeGuard":"function isPosixRelativePath(path: string): boolean {\n  return !path.includes('\\\\') && !path.startsWith('-') && !path.split('/').some((part) => part === '' || part === '.' || part === '..');\n}","tryCatchPattern":"try {\n  const state = captureSourceState(repoRoot);\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('ambiguous repository path separator')) {\n    const path = error.message.slice('ambiguous repository path separator: '.length);\n    throw new Error(`rename file containing a backslash (git mv): ${JSON.stringify(path)}`);\n  }\n  throw error;\n}","preventionTips":["Build repository-relative paths with path.posix.join, never with backslash joins","Fix generators from Windows environments that emit literal backslash filenames","Scan git ls-files for backslashes in CI"],"tags":["git","path","cross-platform","windows","repository-state"],"backgroundTag":"path-separator-mismatch","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"}