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
- Search the failing middleware for every `next()` call and ensure exactly one executes per request
- Don't call next() in finally or in both try/catch branches; call it once before other logic
- Return or branch after calling next(): `await next(); return c.text(...)`
- 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
- Call next() exactly once per middleware; never in finally
- grep every custom middleware for 'next(' and count call sites
- Return immediately after awaiting next() when handling post-logic
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
- Context is not finalized. Did you forget to return a Respons
- Unmet condition
- basic auth middleware requires options for "username and pas
- bearer auth middleware requires options for "token" or "veri
- Middleware vary configuration cannot include "*", as it disa
AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28).
Data as JSON: /api/errors/6b028babeb26757d.
Report an issue: GitHub.