honojs/hono · error · Error

next() called multiple times

Error message

next() called multiple times

What it means

Hono's compose() guards against middleware calling `next()` more than once per invocation. Each middleware gets a one-shot next(); dispatch tracks the current index and throws if a later dispatch has an index not greater than the last — which happens when a single execution path awaits next() twice, or two branches of the same middleware both call next().

Source

Thrown at src/compose.ts:34

  middleware: [[Function, unknown], unknown][] | [[Function]][],
  onError?: ErrorHandler<E>,
  onNotFound?: NotFoundHandler<E>
): ((context: Context, next?: Next) => Promise<Context>) => {
  return (context, next) => {
    let index = -1

    return dispatch(0)

    /**
     * Dispatch the middleware functions.
     *
     * @param {number} i - The current index in the middleware array.
     *
     * @returns {Promise<Context>} - A promise that resolves to the context.
     */
    async function dispatch(i: number): Promise<Context> {
      if (i <= index) {
        throw new Error('next() called multiple times')
      }
      index = i

      let res
      let isError = false
      let handler

      if (middleware[i]) {
        handler = middleware[i][0][0]
        context.req.routeIndex = i
      } else {
        handler = (i === middleware.length && next) || undefined
      }

      if (handler) {
        try {
          res = await handler(context, () => dispatch(i + 1))
        } catch (err) {

View on GitHub (pinned to e2740d5a1b)

Solutions

  1. Search the failing middleware for every `next()` call and ensure exactly one executes per request
  2. Don't call next() in finally or in both try/catch branches; call it once before other logic
  3. Return or branch after calling next(): `await next(); return c.text(...)`
  4. Reproduce with a log/marker in each middleware to identify which one double-calls

Example fix

// before
app.use(async (c, next) => {
  try {
    await next()
  } catch {
    await next() // second call -> 'next() called multiple times'
  }
})

// after
app.use(async (c, next) => {
  try {
    await next()
  } catch (err) {
    // handle error; do NOT call next() again
  }
})
Defensive patterns

Strategy: validation

Validate before calling

app.use(async (c, next) => {
  let called = false
  const once = () => {
    if (called) throw new Error('next() called multiple times')
    called = true
    return next()
  }
  // pass `once` instead of next to downstream logic
})

Try / catch

try { await handler(c, next) } catch (e) { if (e instanceof Error && e.message === 'next() called multiple times') { /* fix the middleware that double-calls next */ } throw e }

Prevention

When it happens

Trigger: Calling `await next()` twice in one middleware body (including in both a try and a catch/finally path that both run), calling next() without await and then calling it again, or recursive/branching code inside a handler that reaches multiple next() calls for one request.

Common situations: Copy-pasted middleware that calls next() in both try and catch; race conditions where a timeout branch also calls next(); refactors that accidentally leave a stray next() call; conditional code paths that both execute (e.g. next() inside if and after the if).

Related errors


AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28). Data as JSON: /api/errors/6b028babeb26757d. Report an issue: GitHub.