remix-run/react-router · error · Error
Module cannot have both an ErrorBoundary export and a Server
Error message
Module cannot have both an ErrorBoundary export and a ServerErrorBoundary export
What it means
Runtime guard in frameworkRoute(): a route module cannot export both `ErrorBoundary` (client) and `ServerErrorBoundary` (server). Thrown when both are truthy at module load inside the lazy resolver. The framework picks one error boundary per route; coexistence is ambiguous.
Source
Thrown at packages/react-router-dev/vite/rsc/virtual-route-config.ts:40
if ("default" in mod && mod.default) {
if ("ServerComponent" in mod && mod.ServerComponent) {
throw new Error("Module cannot have both a default export and a ServerComponent export");
}
Component = mod.default;
} else if ("ServerComponent" in mod && mod.ServerComponent) {
Component = mod.ServerComponent;
}
if ("Layout" in mod && mod.Layout) {
if ("ServerLayout" in mod && mod.ServerLayout) {
throw new Error("Module cannot have both a Layout export and a ServerLayout export");
}
Layout = mod.Layout;
} else if ("ServerLayout" in mod && mod.ServerLayout) {
Layout = mod.ServerLayout;
}
if ("ErrorBoundary" in mod && mod.ErrorBoundary) {
if ("ServerErrorBoundary" in mod && mod.ServerErrorBoundary) {
throw new Error(
"Module cannot have both an ErrorBoundary export and a ServerErrorBoundary export",
);
}
ErrorBoundary = mod.ErrorBoundary;
} else if ("ServerErrorBoundary" in mod && mod.ServerErrorBoundary) {
ErrorBoundary = mod.ServerErrorBoundary;
}
if ("HydrateFallback" in mod && mod.HydrateFallback) {
if ("ServerHydrateFallback" in mod && mod.ServerHydrateFallback) {
throw new Error(
"Module cannot have both a HydrateFallback export and a ServerHydrateFallback export",
);
}
HydrateFallback = mod.HydrateFallback;
} else if ("ServerHydrateFallback" in mod && mod.ServerHydrateFallback) {
HydrateFallback = mod.ServerHydrateFallback;
}
View on GitHub (pinned to 1fd704a7da)
Solutions
- Remove one of the two exports — typically keep `ServerErrorBoundary` for RSC routes, or `ErrorBoundary` for client-only routes.
- Run typegen to get the duplicate-export compile-time error and fix all flagged modules.
Example fix
// app/routes/foo.tsx
// before
export function ErrorBoundary() { return <p>client err</p>; }
export function ServerErrorBoundary() { return <p>server err</p>; }
// after (server only)
export function ServerErrorBoundary() { return <p>server err</p>; } Defensive patterns
Strategy: validation
Validate before calling
const src = readFileSync(routeFile, 'utf8');
const hasE = /export\s+(async\s+)?function\s+ErrorBoundary\b/.test(src);
const hasSE = /export\s+(async\s+)?function\s+ServerErrorBoundary\b/.test(src);
if (hasE && hasSE) throw new Error(`${routeFile}: cannot export both ErrorBoundary and ServerErrorBoundary`); Type guard
function moduleExportsOnlyOneErrorBoundary(mod: Record<string, unknown>): boolean {
return !mod.ErrorBoundary || !mod.ServerErrorBoundary;
} Prevention
- Standardize on one error-boundary export per route.
- Add a pre-commit grep for the pair.
When it happens
Trigger: A route module exports `export function ErrorBoundary()` and `export function ServerErrorBoundary()` simultaneously. The `if ('ErrorBoundary' in mod && mod.ErrorBoundary)` branch detects the pair and throws.
Common situations: Migrating an error boundary to RSC and leaving the client version. Copying a sample that had both for documentation purposes.
Related errors
- Module cannot have both a default export and a ServerCompone
- Module cannot have both a Layout export and a ServerLayout e
- Module cannot have both a HydrateFallback export and a Serve
- Invalid route module exports. The following pairs of exports
- Error splitting route module: ${id} ${invalidChunks.map((na
AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12).
Data as JSON: /api/errors/59b23f9a050c19e9.
Report an issue: GitHub.