{"record":{"id":"a517e04465d07633","repo":"windmill-labs/windmill","slug":"found-collisions-length-path-s-that-differ-onl","errorCode":null,"errorMessage":"Found ${collisions.length} path(s) that differ only by letter case:\n${groups}\nOn case-insensitive filesystems (Windows, default macOS) these collapse into a single file/directory and cannot both be synced. Rename one side to a distinct path to make the tree sync reliably across platforms.","messagePattern":"Found (.+?) path\\(s\\) that differ only by letter case:\n(.+?)\nOn case-insensitive filesystems \\(Windows, default macOS\\) these collapse into a single file/directory and cannot both be synced\\. Rename one side to a distinct path to make the tree sync reliably across platforms\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cli/src/commands/sync/sync.ts","lineNumber":2487,"sourceCode":"    const fromPrefix = fromSegs.slice(0, i + 1).join(\"/\");\n    const toPrefix = toSegs.slice(0, i + 1).join(\"/\");\n    const key = `${fromPrefix} -> ${toPrefix}`;\n    if (!seen.has(key)) {\n      seen.add(key);\n      out.push(key);\n    }\n  }\n  return out;\n}\n\n// Emit a single grouped warning for case-only collisions that cannot be\n// represented on a case-insensitive filesystem (two distinct server paths\n// differing only by case). Unlike the drift handled by\n// canonicalizeCaseInsensitiveKeys, these require the user to rename one side.\nfunction warnUnrepresentableCaseCollisions(collisions: string[][]): void {\n  if (collisions.length === 0) return;\n  const groups = collisions.map((g) => `  - ${g.join(\"  <->  \")}`).join(\"\\n\");\n  log.warn(\n    `Found ${collisions.length} path(s) that differ only by letter case:\\n` +\n      `${groups}\\n` +\n      `On case-insensitive filesystems (Windows, default macOS) these collapse ` +\n      `into a single file/directory and cannot both be synced. Rename one side ` +\n      `to a distinct path to make the tree sync reliably across platforms.`,\n  );\n}\n\n// Probe (and cache) whether `dir` lives on a case-insensitive filesystem.\n// Auto-detected by round-tripping a probe file under two casings, with an\n// explicit WMILL_CASE_INSENSITIVE_FS=true/false override so Windows behaviour\n// can be forced (or emulated for cross-platform repos / tests) on any host.\nlet _caseInsensitiveFsCache: boolean | undefined;\nexport async function isCaseInsensitiveFilesystem(\n  dir: string,\n): Promise<boolean> {\n  const override = (process.env.WMILL_CASE_INSENSITIVE_FS ?? \"\")\n    .trim()","sourceCodeStart":2469,"sourceCodeEnd":2505,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/cli/src/commands/sync/sync.ts#L2469-L2505","documentation":"A warning from warnUnrepresentableCaseCollisions during sync. The remote workspace contains two or more paths that differ only by letter case (e.g. `Scripts/Foo` and `scripts/foo`). On a case-insensitive filesystem (Windows, default macOS) these collapse into one file, so both cannot be synced to disk; the user must rename one.","triggerScenarios":"`wmill sync pull`/`push` detects, after diffing, remote item groups whose paths collide case-insensitively. Typical origin: items created by different people/automation with differing casing, or renames that only changed case.","commonSituations":"Team members on case-sensitive Linux created both paths; a rename on the server changed only letter case; duplicate scripts created with 'MyScript' vs 'myscript'; CI on Windows/macOS then fails or clobbers files when pulling.","solutions":["Rename one of the colliding items on the server (via UI or `wmill` rename) so paths differ beyond case.","Re-run the sync; the warning disappears once no case-only collisions remain.","Prevent recurrence by adopting a naming convention (e.g. lowercase kebab-case paths) enforced in review/CI.","On macOS, if the files already collapsed locally, delete the merged directory and re-pull after fixing the server paths."],"exampleFix":"// before: server has both\nu/admin/hello_script.script/script\nu/admin/Hello_Script.script/script\n// after: rename one side\nu/admin/hello_script.script/script\nu/admin/hello_script_v2.script/script","handlingStrategy":"validation","validationCode":"const seen = new Map<string, string>();\nfor (const p of allRemotePaths) {\n  const key = p.toLowerCase();\n  if (seen.has(key)) throw new Error(`Case collision: ${seen.get(key)} <-> ${p}`);\n  seen.set(key, p);\n}","typeGuard":"function hasCaseCollision(paths: string[]): boolean {\n  const set = new Set(paths.map(p => p.toLowerCase()));\n  return set.size !== paths.length;\n}","tryCatchPattern":"if (hasCaseCollision(remotePaths)) {\n  console.error(\"Rename one side on the server before pulling on case-insensitive OS\");\n} else {\n  await wmill.sync.pull(...);\n}","preventionTips":["Adopt an all-lowercase naming convention for script/flow/folder paths.","Run a case-collision lint in CI before syncing to case-insensitive dev machines.","After case-only renames on the server, delete the collapsed local dir and re-pull.","Develop on a case-sensitive filesystem (or APFS case-sensitive volume) for parity with Linux servers."],"tags":["cli","filesystem","case-sensitivity","sync","cross-platform"],"backgroundTag":"case-insensitive-path-collision","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}