remix-run/react-router · error · Error
You may only call `next()` once per middleware
Error message
You may only call `next()` once per middleware
What it means
Middleware's `next()` function records whether it has already been invoked. Calling `next()` a second time inside the same middleware throws, because double-advancing the chain would duplicate downstream work and break the result/error handling invariants.
Source
Thrown at packages/react-router/lib/router/router.ts:6334
if (request.signal.aborted) {
throw (
request.signal.reason ??
new Error(`Request aborted: ${request.method} ${request.url}`)
);
}
let tuple = middlewares[idx];
if (!tuple) {
// We reached the end of our middlewares, call the handler
let result = await handler();
return result;
}
let [routeId, middleware] = tuple;
let nextResult: { value: Result } | undefined;
let next: MiddlewareNextFunction<Result> = async () => {
if (nextResult) {
throw new Error("You may only call `next()` once per middleware");
}
try {
let result = await callRouteMiddleware(
args,
middlewares,
handler,
processResult,
isResult,
errorHandler,
idx + 1,
);
nextResult = { value: result };
return nextResult.value;
} catch (error) {
nextResult = { value: await errorHandler(error, routeId, nextResult) };
return nextResult.value;View on GitHub (pinned to 1fd704a7da)
Solutions
- Call `next()` exactly once per middleware invocation; store its result if you need to read/modify it.
- Use `try/finally` only for cleanup (no second `next()`); reuse the captured `const result = await next()`.
- Run your middleware in a unit test that asserts the chain executes once.
Example fix
// before
export const middleware = async ({ request }, next) => {
await next();
await next(); // throws
};
// after
export const middleware = async ({ request }, next) => {
const res = await next();
res.headers.set('x-mw', '1');
return res;
}; Defensive patterns
Strategy: validation
Validate before calling
function once<T extends (...a: any[]) => any>(fn: T): T {
let called = false;
return ((...args: any[]) => {
if (called) throw new Error('next() already called');
called = true;
return fn(...args);
}) as T;
}
const safeNext = once(next); Prevention
- Call `next()` exactly once per middleware.
- Reuse `const result = await next()` instead of awaiting twice.
- Unit-test middleware to assert single advancement.
When it happens
Trigger: A route `middleware` export that calls `await next()` twice in the same invocation: once in a try block and once in a finally, or in both branches of an `if/else`, or accidentally after awaiting it.
Common situations: Copy-pasting logging middleware that calls `next()` at the top and again at the bottom; a `try/finally` that calls `next()` in both; refactoring that loses track of which branch already advanced the chain.
Related errors
- No value found for context
- A history only accepts one active listener
- You cannot call `runClientMiddleware()` from a static handle
- Cannot call `runClientMiddleware()` from within an `runClien
AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12).
Data as JSON: /api/errors/14637cdcd771ae97.
Report an issue: GitHub.