{"record":{"id":"17baa7646e10f3ac","repo":"remix-run/remix","slug":"mounts-values-must-not-overlap-received-mount","errorCode":null,"errorMessage":"mounts values must not overlap. Received \"${mount.fileRootValue}\" and \"${otherMount.fileRootValue}\", resolving to \"${mount.fileRoot}\" and \"${otherMount.fileRoot}\".","messagePattern":"mounts values must not overlap\\. Received \"(.+?)\" and \"(.+?)\", resolving to \"(.+?)\" and \"(.+?)\"\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/assets/src/lib/routes.ts","lineNumber":176,"sourceCode":"  return normalizePathname(`${root.replace(/\\/+$/, '')}/${path.replace(/^\\/+/, '')}`)\n}\n\nfunction getPathWithinRoot(root: string, value: string): string | null {\n  if (value === root) return ''\n  if (root === '/') return value.slice(1)\n  if (!value.startsWith(`${root}/`)) return null\n  return value.slice(root.length + 1)\n}\n\nfunction validateNoOverlappingMounts(mounts: readonly CompiledMount[]): void {\n  for (let index = 0; index < mounts.length; index++) {\n    let mount = mounts[index]\n\n    for (let otherIndex = index + 1; otherIndex < mounts.length; otherIndex++) {\n      let otherMount = mounts[otherIndex]\n\n      if (rootsOverlap(mount.fileRoot, otherMount.fileRoot)) {\n        throw new TypeError(\n          `mounts values must not overlap. Received \"${mount.fileRootValue}\" and \"${otherMount.fileRootValue}\", resolving to \"${mount.fileRoot}\" and \"${otherMount.fileRoot}\".`,\n        )\n      }\n    }\n  }\n}\n\nfunction validateNoOverlappingUrlRoots(mounts: readonly CompiledMount[]): void {\n  for (let index = 0; index < mounts.length; index++) {\n    let mount = mounts[index]\n\n    for (let otherIndex = index + 1; otherIndex < mounts.length; otherIndex++) {\n      let otherMount = mounts[otherIndex]\n      if (!rootsOverlap(mount.urlRoot, otherMount.urlRoot)) continue\n\n      throw new TypeError(\n        `mounts keys must not overlap. Received \"${mount.urlRootKey}\" and \"${otherMount.urlRootKey}\".`,\n      )","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/assets/src/lib/routes.ts#L158-L194","documentation":"Two or more mounts resolve to overlapping file system directories (e.g. one is a prefix/ancestor of another after resolving against rootDir). validateNoOverlappingMounts rejects this because file lookup would be ambiguous.","triggerScenarios":"mounts: { '/a': 'public', '/b': 'public/assets' } — 'public/assets' is inside 'public'; also mounts resolving to the same directory, or '.' combined with any subdirectory.","commonSituations":"Adding a more specific mount while keeping the broad one; using '.' or '' as a catch-all mount alongside scoped mounts; different relative spellings ('public' vs 'public/' vs './public') that resolve to the same directory.","solutions":["Remove the redundant broader or narrower mount so each directory is mounted once","Point overlapping mounts at distinct directories","Consolidate and rely on nested URL keys instead of nested file roots"],"exampleFix":"// before\nmounts: { '/': 'public', '/assets': 'public/assets' }\n// after\nmounts: { '/': 'public' }","handlingStrategy":"validation","validationCode":"const resolved = Object.values(mounts).map((v) => resolve(rootDir, v))\nfor (let i = 0; i < resolved.length; i++)\n  for (let j = i + 1; j < resolved.length; j++)\n    if (resolved[i] === resolved[j] || resolved[i].startsWith(resolved[j] + sep) || resolved[j].startsWith(resolved[i] + sep))\n      throw new Error('overlapping mounts')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Mount each directory exactly once","Watch out for '.' catch-all mounts combined with subdirectory mounts"],"tags":["config","routes","mounts","paths"],"backgroundTag":"config-conflict","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}