{"record":{"id":"c18b912bfd941eca","repo":"ruvnet/ruflo","slug":"git-path-is-not-nfc-normalized-path","errorCode":null,"errorMessage":"Git path is not NFC-normalized: ${path}","messagePattern":"Git path is not NFC-normalized: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/codex/src/harness/repository-state.ts","lineNumber":192,"sourceCode":"  const records: Buffer[] = [];\n  let start = 0;\n  for (let index = 0; index < output.length; index += 1) {\n    if (output[index] !== 0) continue;\n    if (index > start) records.push(output.subarray(start, index));\n    start = index + 1;\n  }\n  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}","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/codex/src/harness/repository-state.ts#L174-L210","documentation":"Even with valid UTF-8, decodeGitPath requires every tracked path to be in Unicode Normalization Form C (precomposed): path must equal path.normalize('NFC'). macOS filesystems return decomposed (NFD) names for accented characters, so 'café' stored with a combining accent fails the comparison; the offending path is included in the message. Normalization form is part of path identity because paths feed content digests.","triggerScenarios":"Creating or checking out files with diacritics on macOS (NFD) and then running the harness's source-state snapshot; names synced from iOS clients; NFD names committed by a contributor whose editor or FS decomposes accents.","commonSituations":"Cross-platform teams (macOS NFD vs Linux NFC); cloud-sync folders (iCloud/Dropbox) that decompose names; filenames typed with dead-key input methods that emit combining sequences.","solutions":["Rename offenders to NFC: `convmv -r -f nfd -t nfc --notest .` after reviewing the dry run","Or `git mv` the file to a freshly typed precomposed name","Normalize user-supplied filenames to NFC at ingestion (name.normalize('NFC')) before writing into the repo","Add a CI check that rejects NFD paths in git ls-files output"],"exampleFix":"// before: filename received from macOS upload, stored decomposed\nwriteFileSync(`uploads/${userFileName}`, data); // userFileName is NFD\n\n// after: normalize before it touches the repo\nwriteFileSync(join('uploads', userFileName.normalize('NFC')), data);","handlingStrategy":"try-catch","validationCode":"function findNonNfcPaths(paths: readonly string[]): string[] {\n  return paths.filter((p) => p !== p.normalize('NFC'));\n}","typeGuard":"function isNfcPath(path: string): boolean {\n  return path === path.normalize('NFC');\n}","tryCatchPattern":"try {\n  const state = captureSourceState(repoRoot);\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('Git path is not NFC-normalized')) {\n    const path = error.message.slice('Git path is not NFC-normalized: '.length);\n    throw new Error(`rename to NFC (git mv or convmv -f nfd -t nfc): ${path}`);\n  }\n  throw error;\n}","preventionTips":["Run convmv -f nfd -t nfc (dry run first) on repos touched by macOS/iOS clients","Normalize user-supplied filenames with name.normalize('NFC') before writing them into the repo","Add a CI scan rejecting decomposed paths in git ls-files output"],"tags":["git","unicode","normalization","filename","macos","repository-state"],"backgroundTag":"unicode-normalization-mismatch","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}