remix-run/react-router · error · Error
You cannot call `runClientMiddleware()` from a static handle
Error message
You cannot call `runClientMiddleware()` from a static handler `dataStrategy`. Middleware is run outside of `dataStrategy` during SSR in order to bubble up the Response. You can enable middleware via the `respond` API in `query`/`queryRoute`
What it means
When `createStaticHandler` runs a custom `dataStrategy` during SSR (`isStaticHandler === true`), the `runClientMiddleware` field is a function that always throws. Middleware cannot run from within `dataStrategy` during SSR because the framework must let the Response bubble up through the static handler's `respond` API instead.
Source
Thrown at packages/react-router/lib/router/router.ts:6573
}
// Send all matches here to allow for a middleware-type implementation.
// handler will be a no-op for unneeded routes and we filter those results
// back out below.
let dataStrategyArgs: Omit<
DataStrategyFunctionArgs<unknown>,
"fetcherKey" | "runClientMiddleware"
> = {
request,
url: createDataFunctionUrl(request, path),
pattern: getRoutePattern(matches),
params: matches[0].params,
context: scopedContext,
matches,
};
let runClientMiddleware = isStaticHandler
? () => {
throw new Error(
"You cannot call `runClientMiddleware()` from a static handler " +
"`dataStrategy`. Middleware is run outside of `dataStrategy` during " +
"SSR in order to bubble up the Response. You can enable middleware " +
"via the `respond` API in `query`/`queryRoute`",
);
}
: (cb: DataStrategyFunction<RouterContextProvider>) => {
let typedDataStrategyArgs =
dataStrategyArgs as DataStrategyFunctionArgs<RouterContextProvider>;
return runClientMiddlewarePipeline(typedDataStrategyArgs, () => {
return cb({
...typedDataStrategyArgs,
fetcherKey,
runClientMiddleware: () => {
throw new Error(
"Cannot call `runClientMiddleware()` from within an " +
"`runClientMiddleware` handler",
);View on GitHub (pinned to 1fd704a7da)
Solutions
- Do not call `runClientMiddleware()` from a `dataStrategy` used with `createStaticHandler`.
- Branch the dataStrategy: run middleware on the client only, or drive SSR middleware via the `respond` API in `query`/`queryRoute`.
- Factor the shared logic out so the SSR variant omits the `runClientMiddleware` call.
Example fix
// before
createStaticHandler(routes, {
dataStrategy: (args) => args.runClientMiddleware(() => defaultStrategy(args)),
});
// after
createStaticHandler(routes, {
dataStrategy: defaultStrategy, // no runClientMiddleware on SSR
}); Defensive patterns
Strategy: validation
Validate before calling
function isStaticDataStrategyArgs(args: any): boolean {
return typeof args.runClientMiddleware === 'function' && /static/.test(args.runClientMiddleware.toString());
}
// branch the dataStrategy:
const dataStrategy = (args) =>
isStaticDataStrategyArgs(args)
? defaultStrategy(args)
: args.runClientMiddleware(() => defaultStrategy(args)); Prevention
- Keep one dataStrategy per handler type (static vs client).
- Don't share client-only middleware-driving code with the SSR handler.
- Drive SSR middleware via the `respond` API.
When it happens
Trigger: User-supplied `dataStrategy` calls `args.runClientMiddleware(...)` while the handler is the static (SSR) one created by `createStaticHandler({ dataStrategy })`.
Common situations: Sharing one `dataStrategy` between client and server without branching on context; copy-pasting a client-side middleware-driving dataStrategy into `createStaticHandler`.
Related errors
- ${method}() call aborted without an `AbortSignal.reason`: ${
- Cannot call `runClientMiddleware()` from within an `runClien
- No handlers were found for the request: ${url.pathname}${url
- Prerender (data): Received a ${response.status} status code
- Cannot write to a destroyed or ended writable stream
AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12).
Data as JSON: /api/errors/8fa3563f438f11e7.
Report an issue: GitHub.