remix-run/react-router · error

Cannot call `runClientMiddleware()` from within an…

Error message

Cannot call `runClientMiddleware()` from within an `runClientMiddleware` handler

What it means

The callback you pass to runClientMiddleware(cb) receives args whose runClientMiddleware is intentionally a throwing stub (lib/router/router.ts:6588): nesting is disallowed because the outer call is already driving the middleware pipeline for this request/fetcher scope.

Solutions

  1. Flatten the logic: make a single runClientMiddleware call at the top of your dataStrategy
  2. Move shared inner work into plain functions that take args, not into another middleware pipeline
  3. Delete the nested wrapper call and reuse the result of the single outer call

Example fix

// before
args.runClientMiddleware(async (a) => {
  return a.runClientMiddleware(async () => doWork(a)) // throws
})
// after
args.runClientMiddleware(async (a) => doWork(a))
Defensive patterns

Strategy: validation

Validate before calling

// Structural rule: call runClientMiddleware once per (request, fetcherKey) scope
let wrapped = false;
const dataStrategy = async (args) => {
  if (!wrapped) { wrapped = true; return args.runClientMiddleware(() => doWork(args)) }
  return doWork(args);
};

Prevention

When it happens

Trigger: Inside args.runClientMiddleware(async (ctx) => { ... }), calling ctx.runClientMiddleware(...) (or the outer one captured earlier) again for a nested fetch; recursive helper functions that wrap each level in runClientMiddleware.

Common situations: Composing several middleware-wrapped operations in one dataStrategy; generic wrappers (tracing, auth refresh) applied at multiple layers of the same call tree.

Related errors


AI-assisted analysis of remix-run/react-router@7aea711dd1 (2026-08-18). Data as JSON: /api/errors/9175c9b3c641876d. Report an issue: GitHub.

Appendix: source

Thrown at packages/react-router/lib/router/router.ts:6567

  };
  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",
              );
            },
          });
        });
      };

  let results = await dataStrategyImpl({
    ...dataStrategyArgs,
    fetcherKey,
    runClientMiddleware,
  });

  // Wait for all routes to load here but swallow the error since we want
  // it to bubble up from the `await loadRoutePromise` in `callLoaderOrAction` -
  // called from `match.resolve()`. We also ensure that all promises are
  // awaited so that we don't inadvertently leave any hanging promises.

View on GitHub (pinned to 7aea711dd1)