{"record":{"id":"77e3f6c5b3b95a3c","repo":"remix-run/remix","slug":"middleware-must-return-a-response-or-call-next","errorCode":null,"errorMessage":"Middleware must return a Response or call next()","messagePattern":"Middleware must return a Response or call next\\(\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/fetch-router/src/lib/middleware.ts","lineNumber":142,"sourceCode":"    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)\n\n    // If a response was returned, short-circuit the chain\n    if (response instanceof Response) {\n      return response\n    }\n\n    // If the middleware called next(), use the downstream response\n    if (nextPromise != null) {\n      return nextPromise\n    }\n\n    throw new Error('Middleware must return a Response or call next()')\n  }\n\n  return dispatch(0)\n}\n","sourceCodeStart":124,"sourceCodeEnd":147,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/fetch-router/src/lib/middleware.ts#L124-L147","documentation":"A middleware function must either return a Response (short-circuiting the chain) or call next() to delegate to downstream handlers. This error is thrown when the middleware returns undefined or another non-Response value and never called next().","triggerScenarios":"A middleware that forgets `return` on some path, does async work but returns nothing, or returns a mutated body/ non-Response object.","commonSituations":"Refactoring handlers into middleware and dropping the return statement; early-return guards (auth checks) that log or set state but neither respond nor call next().","solutions":["Add `return await next()` at the end of the middleware","For short-circuiting (auth, redirects), return a Response: return redirect('/login')","Check every code path (if/else, try/finally) returns a Response or calls next()"],"exampleFix":"// before\nasync function auth(ctx, next) {\n  if (!ctx.session) redirect('/login')\n  await next() // not returned\n}\n// after\nasync function auth(ctx, next) {\n  if (!ctx.session) return redirect('/login')\n  return next()\n}","handlingStrategy":"validation","validationCode":"// ensure every path returns\nconst mw = async (ctx, next) => (condition ? new Response('ok') : next())","typeGuard":"const isResponse = (v: unknown): v is Response => v instanceof Response","tryCatchPattern":"try { return await middleware(ctx, next) } catch (e) { if (e instanceof Error && /Middleware must return/.test(e.message)) { return new Response('middleware error', { status: 500 }) } throw e }","preventionTips":["End middleware bodies with `return next()` or `return someResponse`","Lint for middleware functions missing a return on some path"],"tags":["middleware","fetch-router","request-handling"],"backgroundTag":"middleware-no-response-or-next","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}