{"record":{"id":"595bf93b7ef86421","repo":"remix-run/react-router","slug":"unable-to-define-routes-with-duplicate-route-id","errorCode":null,"errorMessage":"Unable to define routes with duplicate route id: \"${id}\"","messagePattern":"Unable to define routes with duplicate route id: \"(.+?)\"","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-router-dev/config/routes.ts","lineNumber":377,"sourceCode":"  routes: RouteConfigEntry[],\n): RouteManifest {\n  let routeManifest: RouteManifest = {};\n\n  function walk(route: RouteConfigEntry, parentId?: string) {\n    let id = route.id || createRouteId(route.file);\n    let manifestItem: RouteManifestEntry = {\n      id,\n      parentId,\n      file: Path.isAbsolute(route.file)\n        ? Path.relative(appDirectory, route.file)\n        : route.file,\n      path: route.path,\n      index: route.index,\n      caseSensitive: route.caseSensitive,\n    };\n\n    if (routeManifest.hasOwnProperty(id)) {\n      throw new Error(\n        `Unable to define routes with duplicate route id: \"${id}\"`,\n      );\n    }\n    routeManifest[id] = manifestItem;\n\n    if (route.children) {\n      for (let child of route.children) {\n        walk(child, id);\n      }\n    }\n  }\n\n  for (let route of routes) {\n    walk(route);\n  }\n\n  return routeManifest;\n}","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/remix-run/react-router/blob/6beaca39526d5716c3c112ebb0782765baa5a9ce/packages/react-router-dev/config/routes.ts#L359-L395","documentation":"configRoutesToRouteManifest() assigns each route an id — explicitly via route.id, otherwise the file path with its extension stripped and normalized (createRouteId). Before inserting into the manifest it checks for an existing entry with the same id and throws on the first duplicate, because the manifest is keyed by id and ambiguity would break route generation. In Framework Mode this surfaces while processing routes.ts (flatRoutes output or manual config).","triggerScenarios":"Referencing the same file from two route() calls (e.g. a shared layout used at two paths) without distinct explicit ids; two route files differing only by extension (routes.ts and routes.tsx) or by case on case-insensitive filesystems; duplicate explicit id values; flatRoutes configs where filenames collide after extension stripping.","commonSituations":"Reusing one component file for multiple routes during refactors; macOS/Windows development hiding case-only duplicates that explode on Linux CI; renames that leave both old and new entries in the config.","solutions":["Give one of the colliding routes an explicit unique id: route(\"a\", \"./x.tsx\", { id: \"x-a\" })","If two files collide (differing extensions or case), delete or rename one of them","Search your routes config / app/routes files for the id quoted in the message to locate both definitions"],"exampleFix":"// before: same file twice -> duplicate id \"routes/layout\"\nimport type { RouteConfig } from \"@react-router/dev/routes\";\nexport default [\n  route(\"a\", \"./layout.tsx\", [index(\"./a.tsx\")]),\n  route(\"b\", \"./layout.tsx\", [index(\"./b.tsx\")]),\n] satisfies RouteConfig;\n\n// after: explicit unique ids\nexport default [\n  route(\"a\", \"./layout.tsx\", { id: \"layout-a\" }, [index(\"./a.tsx\")]),\n  route(\"b\", \"./layout.tsx\", { id: \"layout-b\" }, [index(\"./b.tsx\")]),\n] satisfies RouteConfig;","handlingStrategy":"validation","validationCode":"import type { RouteConfig } from \"@react-router/dev/routes\";\n\nfunction assertUniqueRouteIds(routes: RouteConfig): void {\n  const seen = new Set<string>();\n  const walk = (route: RouteConfig[number]): void => {\n    const id = route.id ?? route.file.replace(/\\.[a-z0-9]+$/i, \"\");\n    if (seen.has(id)) throw new Error(`Duplicate route id: \"${id}\" (from ${route.file})`);\n    seen.add(id);\n    route.children?.forEach(walk);\n  };\n  routes.forEach(walk);\n}\n\nconst config = flatRoutes() satisfies RouteConfig;\nassertUniqueRouteIds(config);\nexport default config;","typeGuard":"function isRouteConfigEntry(v: unknown): v is { id?: string; file: string; children?: unknown[] } {\n  return typeof v === \"object\" && v !== null && typeof (v as { file?: unknown }).file === \"string\";\n}","tryCatchPattern":null,"preventionTips":["When reusing one file for multiple routes, always pass distinct explicit { id: \"...\" } options","Avoid route filenames that differ only by extension or letter case","Run configRoutesToRouteManifest (or the assert above) in a CI check to catch duplicates before build"],"tags":["routes","framework-mode","route-config","duplicate-id"],"backgroundTag":"duplicate-route-id","analyzedSha":"6beaca39526d5716c3c112ebb0782765baa5a9ce","analyzedAt":"2026-08-18T18:04:14.938Z","contentChangedAt":"2026-08-18T18:04:14.938Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}