{"record":{"id":"ab9df8eb6834a908","repo":"remix-run/remix","slug":"invalid-route-map-value-at-location-expected","errorCode":null,"errorMessage":"Invalid route map value at \"${location}\". Expected a nested route object.","messagePattern":"Invalid route map value at \"(.+?)\"\\. Expected a nested route object\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/lib/load-route-map-worker.ts","lineNumber":43,"sourceCode":"\nasync function loadRawRouteTree(routesFile: string): Promise<RawRouteTreeNode[]> {\n  let routeModule: object = await import(pathToFileURL(routesFile).href)\n\n  if (!('routes' in routeModule)) {\n    throw new Error(`Route module ${routesFile} must export a named \"routes\" value.`)\n  }\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 {","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/cli/src/lib/load-route-map-worker.ts#L25-L61","documentation":"Every value in the routes map must be a plain object (a route definition or a nested group). When a non-object (string, number, array, null, function) is found at the indicated location, the worker throws this validation error.","triggerScenarios":"A value inside the routes map that is not a plain object, e.g. `export let routes = { home: '/' }` or `{ home: [HomeRoute] }`, where the string/array is used where a route object is expected.","commonSituations":"Assuming string paths are allowed (`{ home: '/' }`), leaving TODO placeholders, or copying a config format from another framework into app/routes.ts.","solutions":["Replace the non-object value with a proper route object, e.g. `{ home: { file: 'home.tsx' } }` per the route-map schema","Use a nested object for groups, never strings or arrays","Run `remix routes` after each edit to validate the map"],"exampleFix":"// before\nexport let routes = { home: '/' }\n// after\nexport let routes = { home: { index: true, file: 'home.tsx' } }","handlingStrategy":"type-guard","validationCode":"let isPlainObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v)\nif (!isPlainObject(routes)) throw new Error('routes must be an object')","typeGuard":"function isPlainObject(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v)\n}","tryCatchPattern":null,"preventionTips":["Never use bare strings as route values","Validate the config in CI with `remix routes`","Use TypeScript typing for the routes map to catch shape errors at compile time"],"tags":["routes","config","validation"],"backgroundTag":"config-validation-failed","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}