{"record":{"id":"25c31a0098dccb77","repo":"remix-run/remix","slug":"mounts-keys-must-not-overlap-received-mount-ur","errorCode":null,"errorMessage":"mounts keys must not overlap. Received \"${mount.urlRootKey}\" and \"${otherMount.urlRootKey}\".","messagePattern":"mounts keys must not overlap\\. Received \"(.+?)\" and \"(.+?)\"\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/assets/src/lib/routes.ts","lineNumber":192,"sourceCode":"\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      )\n    }\n  }\n}\n\nfunction rootsOverlap(root: string, otherRoot: string): boolean {\n  return (\n    root === otherRoot ||\n    root === '/' ||\n    otherRoot === '/' ||\n    root.startsWith(`${otherRoot}/`) ||\n    otherRoot.startsWith(`${root}/`)\n  )\n}\n\nfunction isUnresolvedPathError(error: unknown, filePath: string): boolean {\n  // Windows reports UNKNOWN rather than ENOENT when a UNC share cannot be reached.","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/assets/src/lib/routes.ts#L174-L210","documentation":"Two or more mounts keys (URL roots) overlap after normalization — one URL root is a prefix of another (or they are equal). validateNoOverlappingUrlRoots rejects this during compileRoutes because request routing would be ambiguous.","triggerScenarios":"mounts: { '/assets': 'a', '/assets/img': 'b' } — '/assets/img' is nested under '/assets'; also duplicate keys after trailing-slash normalization like '/assets' and '/assets/'.","commonSituations":"Adding a nested URL scope for a different directory while the parent URL is already mounted; trailing-slash or basePath inconsistencies making two keys normalize identically; evolving config where an old mount was left in place.","solutions":["Remove the overlapping parent or child URL mount and serve nested paths from the parent's directory structure","If you need a different directory for a sub-path, restructure so URL roots are siblings, not nested","Check for trailing-slash duplicates when generating mounts programmatically"],"exampleFix":"// before\nmounts: { '/assets': 'public/assets', '/assets/img': 'img' }\n// after\nmounts: { '/assets': 'public/assets', '/img': 'img' }","handlingStrategy":"validation","validationCode":"const roots = Object.keys(mounts).map((k) => k.replace(/\\/+$/, '') || '/')\nfor (let i = 0; i < roots.length; i++)\n  for (let j = i + 1; j < roots.length; j++)\n    if (roots[i] === roots[j] || roots[j].startsWith(roots[i] + '/'))\n      throw new Error('overlapping mount keys')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep URL mount keys as siblings, never nested","Strip trailing slashes when generating keys programmatically"],"tags":["config","routes","mounts","url"],"backgroundTag":"config-conflict","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}