remix-run/remix · error · Error

Route module ${routesFile} must export a named "routes" valu

Error message

Route module ${routesFile} must export a named "routes" value.

What it means

The route-map worker imports your app/routes.ts module and requires it to export a named `routes` value (the framework 3 config-based routing entry point). If the module has no `routes` export, the worker throws before parsing anything.

Source

Thrown at packages/cli/src/lib/load-route-map-worker.ts:30

  setExitCode(1)
})

async function run(): Promise<void> {
  let routesFile = process.argv[2]
  if (typeof routesFile !== 'string' || routesFile.length === 0) {
    throw new Error('Missing app/routes.ts path.')
  }

  let tree = await loadRawRouteTree(routesFile)
  process.stdout.write(JSON.stringify(tree))
}

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) {

View on GitHub (pinned to 9696913134)

Solutions

  1. Add a named `routes` export: `export let routes = {...}` in app/routes.ts
  2. If using an empty route set temporarily, export an empty object `export let routes = {}`
  3. Check for accidental `default` export and move it to a named `routes` export

Example fix

// before (app/routes.ts)
export default { root: '/' }
// after
export let routes = { root: '/' }
Defensive patterns

Strategy: type-guard

Validate before calling

let mod = await import(pathToFileURL(routesPath).href)
if (!('routes' in mod)) throw new Error('app/routes.ts must export `routes`')

Type guard

function hasRoutesExport(mod: object): mod is { routes: unknown } {
  return 'routes' in mod
}

Prevention

When it happens

Trigger: app/routes.ts (or the configured route-map file) exports only `default`, only types, or nothing at all — e.g. `export default [{ path: '/' }]` instead of `export let routes = {...}`.

Common situations: Migrating from file-based routing or React Router style config and keeping a default export; leftover placeholder routes.ts created empty; typos like `export const route =` (singular).

Related errors


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