{"record":{"id":"603aa8017793ca71","repo":"remix-run/remix","slug":"detected-a-route-map-cycle-at-currentpath-via","errorCode":null,"errorMessage":"Detected a route map cycle at \"${currentPath}\" via \"${existingPath}\".","messagePattern":"Detected a route map cycle at \"(.+?)\" via \"(.+?)\"\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/lib/load-route-map-worker.ts","lineNumber":49,"sourceCode":"  }\n\n  return normalizeRouteGroup(routeModule.routes, [], new Map())\n}\n\nfunction normalizeRouteGroup(\n  value: unknown,\n  parentSegments: string[],\n  seen: Map<object, string>,\n): RawRouteTreeNode[] {\n  if (!isPlainObject(value)) {\n    let location = parentSegments.length === 0 ? 'routes' : parentSegments.join('.')\n    throw new Error(`Invalid route map value at \"${location}\". Expected a nested route object.`)\n  }\n\n  let existingPath = seen.get(value)\n  let currentPath = parentSegments.length === 0 ? 'routes' : parentSegments.join('.')\n  if (existingPath != null) {\n    throw new Error(`Detected a route map cycle at \"${currentPath}\" via \"${existingPath}\".`)\n  }\n\n  seen.set(value, currentPath)\n\n  try {\n    return Object.entries(value).map(([key, entryValue]) => {\n      let nameSegments = [...parentSegments, key]\n      let name = nameSegments.join('.')\n      let routeLeaf = readRouteLeaf(entryValue)\n\n      if (routeLeaf !== undefined) {\n        return {\n          children: [],\n          key,\n          kind: 'route',\n          method: routeLeaf.method,\n          name,\n          pattern: routeLeaf.pattern,","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/cli/src/lib/load-route-map-worker.ts#L31-L67","documentation":"The routes map is traversed recursively with a `seen` map; if the same object reference is reached twice, your config contains a cycle (direct or indirect self-reference) and the worker aborts instead of infinite-looping.","triggerScenarios":"An object in the routes map referencing itself: `let group = { nested: group }` or two objects referencing each other via getter/spread chains that preserve identity.","commonSituations":"Building route groups programmatically with variables and accidentally assigning a parent into its own children; using shared constant objects that get inserted at multiple nesting levels via assignment (fine with spread, a cycle with direct reference).","solutions":["Find the reported path (e.g. 'routes.admin') and remove the self/indirect reference","If reusing a subtree, clone it with structuredClone or spread (`{ ...shared }`) instead of referencing the same object","Avoid `obj.child = obj` patterns when assembling groups programmatically"],"exampleFix":"// before\nlet admin = {}; admin.self = admin\nexport let routes = { admin }\n// after\nexport let routes = { admin: {} }","handlingStrategy":"validation","validationCode":"function assertAcyclic(node, seen = new Set()) {\n  if (seen.has(node)) throw new Error('cycle')\n  seen.add(node)\n  for (let k in node) if (typeof node[k] === 'object') assertAcyclic(node[k], seen)\n  seen.delete(node)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Clone shared subtrees with spread or structuredClone","Never assign a parent object into its own descendants","Prefer static literal route maps over programmatic assembly"],"tags":["routes","cycle","config"],"backgroundTag":"config-circular-reference","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}