{"record":{"id":"8fa3563f438f11e7","repo":"remix-run/react-router","slug":"you-cannot-call-runclientmiddleware-from-a-sta","errorCode":null,"errorMessage":"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`","messagePattern":"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`","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/react-router/lib/router/router.ts","lineNumber":6552,"sourceCode":"  }\n\n  // Send all matches here to allow for a middleware-type implementation.\n  // handler will be a no-op for unneeded routes and we filter those results\n  // back out below.\n  let dataStrategyArgs: Omit<\n    DataStrategyFunctionArgs<unknown>,\n    \"fetcherKey\" | \"runClientMiddleware\"\n  > = {\n    request,\n    url: createDataFunctionUrl(request, path),\n    pattern: getRoutePattern(matches),\n    params: matches[0].params,\n    context: scopedContext,\n    matches,\n  };\n  let runClientMiddleware = isStaticHandler\n    ? () => {\n        throw new Error(\n          \"You cannot call `runClientMiddleware()` from a static handler \" +\n            \"`dataStrategy`. Middleware is run outside of `dataStrategy` during \" +\n            \"SSR in order to bubble up the Response.  You can enable middleware \" +\n            \"via the `respond` API in `query`/`queryRoute`\",\n        );\n      }\n    : (cb: DataStrategyFunction<RouterContextProvider>) => {\n        let typedDataStrategyArgs =\n          dataStrategyArgs as DataStrategyFunctionArgs<RouterContextProvider>;\n        return runClientMiddlewarePipeline(typedDataStrategyArgs, () => {\n          return cb({\n            ...typedDataStrategyArgs,\n            fetcherKey,\n            runClientMiddleware: () => {\n              throw new Error(\n                \"Cannot call `runClientMiddleware()` from within an \" +\n                  \"`runClientMiddleware` handler\",\n              );","sourceCodeStart":6534,"sourceCodeEnd":6570,"githubUrl":"https://github.com/remix-run/react-router/blob/7aea711dd1ae2bc5a076d13ff17291829690fa74/packages/react-router/lib/router/router.ts#L6534-L6570","documentation":"In SSR, middleware runs outside dataStrategy so that middleware Responses (redirects, errors) can bubble up as the HTTP response. A static handler's dataStrategy therefore receives a runClientMiddleware that is a throwing stub (lib/router/router.ts:6573); calling it means a browser-oriented dataStrategy was reused on the server.","triggerScenarios":"Sharing one custom dataStrategy between createBrowserRouter and createStaticHandler and unconditionally calling args.runClientMiddleware(...); a fetcher/handler strategy copied from client code into an express/node SSR setup; invoking the single-fetch default strategy shape inside query() with middleware enabled.","commonSituations":"Custom dataStrategy with tracing/caching wrappers deployed to both environments; integration tests running the client strategy through a static handler; enabling middleware after the dataStrategy was already written for client-only behavior.","solutions":["Split the strategy per environment: only call runClientMiddleware when running in the browser (typeof window !== 'undefined')","On the server, let query()/queryRoute() drive middleware themselves (respond API) instead of calling it from dataStrategy","Pass a separate dataStrategy option to createStaticHandler vs createBrowserRouter"],"exampleFix":"// before\nconst dataStrategy = async (args) => args.runClientMiddleware(async () => args.fetch())\ncreateBrowserRouter({ dataStrategy })\ncreateStaticHandler({ dataStrategy }) // throws in SSR\n// after\nconst dataStrategy = async (args) =>\n  typeof window === 'undefined'\n    ? args.fetch()\n    : args.runClientMiddleware(async () => args.fetch())","handlingStrategy":"type-guard","validationCode":"const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';\nconst dataStrategy = async (args) =>\n  isBrowser ? args.runClientMiddleware(() => args.fetch()) : args.fetch();","typeGuard":"function isStaticHandlerArgs(args: DataStrategyFunctionArgs): boolean {\n  return typeof window === 'undefined';\n}","tryCatchPattern":null,"preventionTips":["Keep separate dataStrategy implementations for client router and static handler","Branch on environment (window check) before touching runClientMiddleware","On the server, rely on query()/queryRoute() to run middleware outside dataStrategy"],"tags":["ssr","datastrategy","middleware","static-handler","api-misuse"],"backgroundTag":"server-client-api-misuse","analyzedSha":"7aea711dd1ae2bc5a076d13ff17291829690fa74","analyzedAt":"2026-08-18T18:04:14.938Z","contentChangedAt":"2026-08-18T18:04:14.938Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}