remix-run/react-router · error
The "serverBundles" function must only return strings…
Error message
The "serverBundles" function must only return strings containing alphanumeric characters and underscores.
What it means
Beyond being a string, a `serverBundles` return value must match `/^[a-zA-Z0-9_]+$/` because the ID becomes a Vite environment name and a filesystem path segment — hyphens, dots, slashes and unicode are rejected. This guard fires right after the string check, naming the constraint explicitly.
Solutions
- Use only letters, digits, and underscores: rename `'marketing-site'` to `'marketing_site'`
- Sanitize derived IDs: `id.replace(/[^a-zA-Z0-9_]/g, '_')`
- Keep a fixed allowlist of bundle IDs in config rather than generating them from file paths
Example fix
// before
serverBundles: ({ branch }) =>
branch.includes(isAdmin) ? 'admin-bundle' : 'main',
// after
serverBundles: ({ branch }) =>
branch.includes(isAdmin) ? 'admin_bundle' : 'main', Defensive patterns
Strategy: validation
Validate before calling
// Centralize and sanitize bundle IDs
const toBundleId = (raw: string): string => raw.replace(/[^a-zA-Z0-9_]/g, '_');
const serverBundles = async ({ branch }) => {
return toBundleId(branch.at(-1)!.id ?? 'index');
}; Type guard
const isValidBundleId = (v: string): boolean => /^[a-zA-Z0-9_]+$/.test(v);
Try / catch
try {
await build();
} catch (e) {
if (e instanceof Error && e.message.includes('alphanumeric characters and underscores')) {
// replace '-' and '.' in returned bundle IDs with '_'
}
} Prevention
- Use snake_case bundle IDs exclusively
- Sanitize any ID derived from route file paths (replace `/`, `.`, `-`)
- Keep a const allowlist of bundle names in config
When it happens
Trigger: Returning `'marketing-site'`, `'bundle.1'`, or `'fr/français'` from the `serverBundles` function; deriving IDs from route file paths without sanitizing (slashes/dots leak in); locale-based IDs with dashes.
Common situations: Using human-friendly kebab-case bundle names; building IDs from `route.file` substrings that contain `.` (route files like `route._index.tsx`) or `/`.
Related errors
- Route config in " " is invalid.
- The `prerender`/`prerender.paths` config must be a boolean…
- The "serverBundles" function must return a string
- Could not find a root route module in the app directory as
- Custom Vite manifest paths are not supported
AI-assisted analysis of remix-run/react-router@6beaca3952 (2026-08-18).
Data as JSON: /api/errors/4860e26ac7d58958.
Report an issue: GitHub.
Appendix: 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 6beaca3952)