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

  1. Replace the non-object value with a proper route object, e.g. `{ home: { file: 'home.tsx' } }` per the route-map schema
  2. Use a nested object for groups, never strings or arrays
  3. 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

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


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/ab9df8eb6834a908. Report an issue: GitHub.