remix-run/react-router · error · ErrorResponseImpl
You are trying to call ${fn} on a route that does not have a
Error message
You are trying to call ${fn} on a route that does not have a server ${type} (routeId: "${route.id}") What it means
In framework mode, `serverLoader()` and `serverAction()` are only callable from routes whose manifest entry has `hasLoader`/`hasAction` true (i.e., the route module actually exports `loader`/`action`). This guard runs before the single-fetch request is made, logs the message to the console, and throws an `ErrorResponseImpl(400, "Bad Request")` so it surfaces in the route's ErrorBoundary like any other loader/action error.
Source
Thrown at packages/react-router/lib/dom/ssr/routes.tsx:210
groupRoutesByParentId(manifest),
needsRevalidation,
);
}
function preventInvalidServerHandlerCall(
type: "action" | "loader",
route: Omit<EntryRoute, "children">,
) {
if (
(type === "loader" && !route.hasLoader) ||
(type === "action" && !route.hasAction)
) {
let fn = type === "action" ? "serverAction()" : "serverLoader()";
let msg =
`You are trying to call ${fn} on a route that does not have a server ` +
`${type} (routeId: "${route.id}")`;
console.error(msg);
throw new ErrorResponseImpl(400, "Bad Request", new Error(msg), true);
}
}
export function noActionDefinedError(
type: "action" | "clientAction",
routeId: string,
) {
let article = type === "clientAction" ? "a" : "an";
let msg =
`Route "${routeId}" does not have ${article} ${type}, but you are trying to ` +
`submit to it. To fix this, please add ${article} \`${type}\` function to the route`;
console.error(msg);
throw new ErrorResponseImpl(405, "Method Not Allowed", new Error(msg), true);
}
export function createClientRoutes(
manifest: RouteManifest<EntryRoute>,
routeModulesCache: RouteModules,View on GitHub (pinned to 6beaca3952)
Solutions
- Add the missing server export to the route: `export async function loader(args) { ... }` (or `action`).
- If the data is client-only, replace `serverLoader()` with your own fetching logic (e.g., fetch in clientLoader).
- If you just added the loader and still see the error, restart the dev server or rebuild so the manifest `hasLoader` flag is regenerated.
- Move the `serverLoader()` call out of components; it is only valid inside clientLoader/clientAction of a route that has the server handler.
Example fix
// before (route has clientLoader but no loader export)
export async function clientLoader() {
return serverLoader();
}
// after
export async function loader({ request }: LoaderFunctionArgs) {
return getUser(await requireUser(request));
}
export async function clientLoader() {
return serverLoader();
} Defensive patterns
Strategy: try-catch
Try / catch
let data;
try {
data = await serverLoader();
} catch (e) {
if (isResponse(e) && e.status === 400) {
// route has no server loader — degrade gracefully
data = await fetchLocalFallback();
} else {
throw e;
}
} Prevention
- Treat serverLoader()/serverAction() as paired with a loader/action export — add both in the same commit.
- Let the 400 surface through the route ErrorBoundary instead of crashing the tree.
- After adding or moving loaders, restart the dev server so hasLoader flags in the manifest refresh.
When it happens
Trigger: Calling `await serverLoader()` inside a `clientLoader` of a route that has no `loader` export; calling `serverAction()` from a component or clientAction of a route without an `action` export; removing a loader export but leaving the `serverLoader()` call in clientLoader; the manifest being stale after renaming/moving routes.
Common situations: Adding a clientLoader that optimistically calls serverLoader without adding the server loader; refactoring a loader into clientLoader-only and forgetting to delete the serverLoader call; HMR or a stale build manifest claiming a route has no loader right after you add one.
Related errors
- You are trying to call ${fn} on a route that does not have a
- React Router presets must have a `name` property defined.
- Could not find package.json in ${rootDirectory} or any of it
- Unable to define routes with duplicate route id: "${id}"
- The "@mdx-js/rollup" plugin should be placed before the Reac
AI-assisted analysis of remix-run/react-router@6beaca3952 (2026-08-18).
Data as JSON: /api/errors/1b65ca6f2539130b.
Report an issue: GitHub.