{"record":{"id":"4420d4aeb13852ca","repo":"remix-run/remix","slug":"next-called-multiple-times","errorCode":null,"errorMessage":"next() called multiple times","messagePattern":"next\\(\\) called multiple times","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/fetch-router/src/lib/middleware.ts","lineNumber":112,"sourceCode":"  return middleware\n}\n\n/**\n * A function that invokes the next middleware or handler in the chain.\n *\n * @returns The response from the downstream handler\n */\nexport type NextFunction = () => Promise<Response>\n\nexport function runMiddleware(\n  middleware: AnyMiddleware[],\n  context: RequestContext<any, any>,\n  handler: RequestHandler<any>,\n): Promise<Response> {\n  let index = -1\n\n  let dispatch = async (i: number): Promise<Response> => {\n    if (i <= index) throw new Error('next() called multiple times')\n    index = i\n\n    if (context.request.signal.aborted) {\n      throw context.request.signal.reason\n    }\n\n    let fn = middleware[i]\n    if (!fn) {\n      return await raceRequestAbort(Promise.resolve(handler(context)), context.request)\n    }\n\n    let nextPromise: Promise<Response> | undefined\n    let next: NextFunction = () => {\n      nextPromise = dispatch(i + 1)\n      return nextPromise\n    }\n\n    let response = await raceRequestAbort(Promise.resolve(fn(context, next)), context.request)","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/fetch-router/src/lib/middleware.ts#L94-L130","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Restructure the middleware so next() is called exactly once on every path","Await next() once and reuse its Response for any additional logic","For conditional work after downstream handling, capture let res = await next() then post-process"],"exampleFix":"// before\nasync function mw(ctx, next) {\n  if (a) await next()\n  await next() // always runs again\n}\n// after\nasync function mw(ctx, next) {\n  let res = await next()\n  if (a) log(res)\n  return res\n}","handlingStrategy":"validation","validationCode":"let called = false\nconst nextOnce = () => { if (called) throw new Error('double next'); called = true; return next() }","typeGuard":null,"tryCatchPattern":"try { return await next() } catch (e) { if (e instanceof Error && e.message.includes('next() called multiple times')) { /* fix middleware logic */ } throw e }","preventionTips":["Call next() exactly once per middleware; store its Response for reuse","Return the result of next() rather than calling it again in branches"],"tags":["middleware","fetch-router","request-handling"],"backgroundTag":"middleware-next-called-twice","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}