remix-run/remix · error · Error

Middleware must return a Response or call next()

Error message

Middleware must return a Response or call next()

What it means

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().

Source

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

    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)

    // If a response was returned, short-circuit the chain
    if (response instanceof Response) {
      return response
    }

    // If the middleware called next(), use the downstream response
    if (nextPromise != null) {
      return nextPromise
    }

    throw new Error('Middleware must return a Response or call next()')
  }

  return dispatch(0)
}

View on GitHub (pinned to 9696913134)

Solutions

  1. Add `return await next()` at the end of the middleware
  2. For short-circuiting (auth, redirects), return a Response: return redirect('/login')
  3. Check every code path (if/else, try/finally) returns a Response or calls next()

Example fix

// before
async function auth(ctx, next) {
  if (!ctx.session) redirect('/login')
  await next() // not returned
}
// after
async function auth(ctx, next) {
  if (!ctx.session) return redirect('/login')
  return next()
}
Defensive patterns

Strategy: validation

Validate before calling

// ensure every path returns
const mw = async (ctx, next) => (condition ? new Response('ok') : next())

Type guard

const isResponse = (v: unknown): v is Response => v instanceof Response

Try / catch

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 }

Prevention

When it happens

Trigger: A middleware that forgets `return` on some path, does async work but returns nothing, or returns a mutated body/ non-Response object.

Common situations: 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().

Related errors


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