remix-run/react-router · error
You may only call `next()` once per middleware
Error message
You may only call `next()` once per middleware
What it means
Each route middleware receives a next() function that resumes the pipeline at the next middleware or the handler; the guard in callRouteMiddleware (lib/router/router.ts:6334) allows exactly one invocation per middleware execution. Calling it a second time (wrong branch structure, retries, loops) throws this error.
Solutions
- Call next() exactly once per code path: capture const result = await next() and branch on the result instead of re-calling
- Do not call next() in both try and catch of the same try/catch
- For retry semantics, put the retryable work inside the handler/route, not around next()
- Return a value (or throw a Response) instead of calling next() when you want to short-circuit
Example fix
// before (second next() throws)
try {
return await next()
} catch {
return await next()
}
// after
const result = await next()
if (result instanceof Response && result.status === 503) {
// handle or throw, but never call next() again
}
return result Defensive patterns
Strategy: validation
Validate before calling
// Wrap next() so double calls fail fast with your own stack trace
function once<T extends (...a: any[]) => any>(fn: T): T {
let called = false;
return ((...a: Parameters<T>) => {
if (called) throw new Error('next() already called');
called = true;
return fn(...a);
}) as T;
}
export const mw = async ({ next }, _once = once(next)) => { /* use _once everywhere */ } Prevention
- Have exactly one `await next()` call site per middleware and branch on its result
- Never call next() in both the try and catch of the same try/catch
- Put retry logic inside handlers/routes, not around next()
- Short-circuit by returning or throwing a Response instead of extra next() calls
When it happens
Trigger: Both branches of an if/else call next(); calling next() again inside a catch block after the first call failed; a loop that calls next() per iteration; retry logic that awaits next() twice to re-run downstream stages.
Common situations: Auth middleware that calls next() on success and also falls through to a second next(); cache middleware attempting retry on transient errors; refactoring middleware from a wrapper function into next()-style and leaving a stray call.
Related errors
- Cannot call `runClientMiddleware()` from within an…
- No value found for context
- You cannot call `runClientMiddleware()` from a static…
- Failed to clone server response
- Missing body in server response
AI-assisted analysis of remix-run/react-router@7aea711dd1 (2026-08-18).
Data as JSON: /api/errors/14637cdcd771ae97.
Report an issue: GitHub.
Appendix: source
Thrown at packages/react-router/lib/router/router.ts:6313
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 7aea711dd1)