remix-run/react-router · error
Module cannot have both an ErrorBoundary export and a…
Error message
Module cannot have both an ErrorBoundary export and a ServerErrorBoundary export
What it means
In RSC mode a route's error UI can come from the client 'ErrorBoundary' export or the server 'ServerErrorBoundary' export, but not both. The framework-route wrapper throws when both are truthy to prevent an ambiguous error-rendering path — this fires whenever the route module is lazily loaded and both exports exist.
Solutions
- Remove 'ErrorBoundary' if errors for this route should render on the server
- Remove 'ServerErrorBoundary' if the error UI must be interactive on the client
- Keep shared error markup in a separate module imported by the surviving export
Example fix
// before
export function ErrorBoundary() { return <p>Something broke</p>; }
export function ServerErrorBoundary() { return <p>Server error</p>; }
// after
export function ServerErrorBoundary() { return <p>Server error</p>; } Defensive patterns
Strategy: validation
Validate before calling
const PAIRS = [["ErrorBoundary", "ServerErrorBoundary"]] as const;
for (const file of routeFiles) {
const mod: Record<string, unknown> = await import(pathToFileURL(file).href);
for (const [client, server] of PAIRS) {
if (mod[client] && mod[server]) {
throw new Error(`${file}: export either ${client} or ${server}, not both`);
}
}
} Type guard
function hasErrorBoundaryConflict(mod: Record<string, unknown>): boolean {
return Boolean(mod.ErrorBoundary) && Boolean(mod.ServerErrorBoundary);
} Prevention
- Add the server variant only after removing the client one
- Codemod or grep for dual error-boundary exports during RSC migration
- Rely on CI builds to catch the static variant of this check
When it happens
Trigger: A route module exporting both 'export function ErrorBoundary()' and 'export function ServerErrorBoundary()' with both truthy.
Common situations: Adding a server error boundary to a route that already had a client one; copying an RSC example's ServerErrorBoundary into an existing route without removing the old export.
Related errors
- Invalid route module exports. The following pairs of…
- Module cannot have both a default export and a…
- Module cannot have both a HydrateFallback export and a…
- Module cannot have both a Layout export and a ServerLayout…
- Error splitting route module
AI-assisted analysis of remix-run/react-router@6beaca3952 (2026-08-18).
Data as JSON: /api/errors/59b23f9a050c19e9.
Report an issue: GitHub.
Appendix: 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 6beaca3952)