remix-run/react-router · error · Error

The "serverBundles" function must only return strings contai

Error message

The "serverBundles" function must only return strings containing alphanumeric characters and underscores.

What it means

Thrown immediately after the string-typed check on the same value. Server bundle IDs become Vite environment names, which disallow hyphens and other punctuation, so React Router enforces `^[a-zA-Z0-9_]+$`. A return value like `"admin-bundle"`, `"v1.2"`, or one containing slashes/spaces fails the regex and aborts the build.

Source

Thrown at packages/react-router-dev/vite/plugin.ts:3469

  await Promise.all(
    getAddressableRoutes(routes).map(async (route) => {
      let branch = getRouteBranch(routes, route.id);
      let serverBundleId = await serverBundles({
        branch: branch.map((route) =>
          configRouteToBranchRoute({
            ...route,
            // Ensure absolute paths are passed to the serverBundles function
            file: path.join(resolvedAppDirectory, route.file),
          }),
        ),
      });
      if (typeof serverBundleId !== "string") {
        throw new Error(`The "serverBundles" function must return a string`);
      }
      // Server bundle IDs must be valid Vite environment names, so hyphens are not allowed
      if (!/^[a-zA-Z0-9_]+$/.test(serverBundleId)) {
        throw new Error(
          `The "serverBundles" function must only return strings containing alphanumeric characters and underscores.`,
        );
      }
      buildManifest.routeIdToServerBundleId[route.id] = serverBundleId;

      buildManifest.serverBundles[serverBundleId] ??= {
        id: serverBundleId,
        file: normalizePath(
          path.join(
            path.relative(
              rootDirectory,
              path.join(serverBuildDirectory, serverBundleId),
            ),
            reactRouterConfig.serverBuildFile,
          ),
        ),
      };
    }),

View on GitHub (pinned to 1fd704a7da)

Solutions

  1. Use only `[A-Za-z0-9_]` in the returned ID — prefer underscores over hyphens (e.g. `admin_area`).
  2. If deriving from paths, sanitize: `id.replace(/[^a-zA-Z0-9_]/g, "_")`.
  3. Pin the function's return type to a branded `ServerBundleId` type to catch slips.

Example fix

// before
export const serverBundles = ({ branch }) => "admin-bundle";
// after
export const serverBundles = ({ branch }) => "admin_bundle";
Defensive patterns

Strategy: validation

Validate before calling

const VALID = /^[a-zA-Z0-9_]+$/;
const assertValidBundleId = (id: string) => {
  if (!VALID.test(id)) throw new Error(`Invalid bundle id: ${id}`);
};

Type guard

const isValidBundleId = (v: unknown): v is string =>
  typeof v === "string" && /^[a-zA-Z0-9_]+$/.test(v);

Prevention

When it happens

Trigger: Returning a hyphenated, dotted, spaced, or otherwise punctuated bundle ID from `serverBundles`; constructing IDs dynamically from route file paths (which contain `/` and `.`); copy-pasting IDs from a naming convention that uses dashes.

Common situations: Naming bundles by feature with dashes (`admin-area`); using kebab-case from route file paths; internationalized IDs with non-ASCII characters.

Related errors


AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12). Data as JSON: /api/errors/4860e26ac7d58958. Report an issue: GitHub.