remix-run/remix · error · Error
Invalid route map value at "${location}". Expected a nested
Error message
Invalid route map value at "${location}". Expected a nested route object. What it means
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.
Source
Thrown at packages/cli/src/lib/load-route-map-worker.ts:43
async function loadRawRouteTree(routesFile: string): Promise<RawRouteTreeNode[]> {
let routeModule: object = await import(pathToFileURL(routesFile).href)
if (!('routes' in routeModule)) {
throw new Error(`Route module ${routesFile} must export a named "routes" value.`)
}
return normalizeRouteGroup(routeModule.routes, [], new Map())
}
function normalizeRouteGroup(
value: unknown,
parentSegments: string[],
seen: Map<object, string>,
): RawRouteTreeNode[] {
if (!isPlainObject(value)) {
let location = parentSegments.length === 0 ? 'routes' : parentSegments.join('.')
throw new Error(`Invalid route map value at "${location}". Expected a nested route object.`)
}
let existingPath = seen.get(value)
let currentPath = parentSegments.length === 0 ? 'routes' : parentSegments.join('.')
if (existingPath != null) {
throw new Error(`Detected a route map cycle at "${currentPath}" via "${existingPath}".`)
}
seen.set(value, currentPath)
try {
return Object.entries(value).map(([key, entryValue]) => {
let nameSegments = [...parentSegments, key]
let name = nameSegments.join('.')
let routeLeaf = readRouteLeaf(entryValue)
if (routeLeaf !== undefined) {
return {View on GitHub (pinned to 9696913134)
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
Example fix
// before
export let routes = { home: '/' }
// after
export let routes = { home: { index: true, file: 'home.tsx' } } Defensive patterns
Strategy: type-guard
Validate before calling
let isPlainObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v)
if (!isPlainObject(routes)) throw new Error('routes must be an object') Type guard
function isPlainObject(v: unknown): v is Record<string, unknown> {
return typeof v === 'object' && v !== null && !Array.isArray(v)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Assuming string paths are allowed (`{ home: '/' }`), leaving TODO placeholders, or copying a config format from another framework into app/routes.ts.
Understand the failure class
Background: Config validation failed: what "invalid value for {key}" and settings-rejection errors mean across 19 open-source libraries — this error's family across 19 libraries.
Related errors
- Invalid route map value at "${name}". Expected a route or ne
- basePath must be a string
- fingerprint.buildId must be a string
- fingerprint.buildId must be a non-empty string
- files.extensions must be an array
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/ab9df8eb6834a908.
Report an issue: GitHub.