remix-run/remix · error · Error

next() called multiple times

Error message

next() called multiple times

What it means

Express-style middleware in fetch-router receives a next() function; calling next() more than once per middleware breaks the dispatch chain, so the runner guards it with an index check and throws this error. Each middleware may advance to the next layer exactly one time.

Source

Thrown at packages/fetch-router/src/lib/middleware.ts:112

  return middleware
}

/**
 * A function that invokes the next middleware or handler in the chain.
 *
 * @returns The response from the downstream handler
 */
export type NextFunction = () => Promise<Response>

export function runMiddleware(
  middleware: AnyMiddleware[],
  context: RequestContext<any, any>,
  handler: RequestHandler<any>,
): Promise<Response> {
  let index = -1

  let dispatch = async (i: number): Promise<Response> => {
    if (i <= index) throw new Error('next() called multiple times')
    index = i

    if (context.request.signal.aborted) {
      throw context.request.signal.reason
    }

    let fn = middleware[i]
    if (!fn) {
      return await raceRequestAbort(Promise.resolve(handler(context)), context.request)
    }

    let nextPromise: Promise<Response> | undefined
    let next: NextFunction = () => {
      nextPromise = dispatch(i + 1)
      return nextPromise
    }

    let response = await raceRequestAbort(Promise.resolve(fn(context, next)), context.request)

View on GitHub (pinned to 9696913134)

Solutions

  1. Restructure the middleware so next() is called exactly once on every path
  2. Await next() once and reuse its Response for any additional logic
  3. For conditional work after downstream handling, capture let res = await next() then post-process

Example fix

// before
async function mw(ctx, next) {
  if (a) await next()
  await next() // always runs again
}
// after
async function mw(ctx, next) {
  let res = await next()
  if (a) log(res)
  return res
}
Defensive patterns

Strategy: validation

Validate before calling

let called = false
const nextOnce = () => { if (called) throw new Error('double next'); called = true; return next() }

Try / catch

try { return await next() } catch (e) { if (e instanceof Error && e.message.includes('next() called multiple times')) { /* fix middleware logic */ } throw e }

Prevention

When it happens

Trigger: Calling await next() twice in one middleware, or calling next() in a loop/recursive helper; also calling a stale next() captured earlier after the chain moved on.

Common situations: Middleware that branches (if/else both call next), retries, or fire-and-forget code paths that invoke next() inside setTimeout/event handlers without awaiting exactly once.

Related errors


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/4420d4aeb13852ca. Report an issue: GitHub.