honojs/hono · error · TypeError

env has to include the 2nd argument of fetch.

Error message

env has to include the 2nd argument of fetch.

What it means

The Bun WebSocket upgrade helper requires Bun's `Server` object (passed as the second argument to fetch() by Bun.serve) in order to call server.upgrade(). If the app is not mounted via `Bun.serve({ fetch: app.fetch })`, `c.env.server` is undefined and the WebSocket upgrade path cannot proceed.

Source

Thrown at src/adapter/bun/websocket.ts:59

    protocol: ws.data.protocol,
    close(code, reason) {
      ws.close(code, reason)
    },
  })
}

export const upgradeWebSocket: UpgradeWebSocket<any> = defineWebSocketHelper((c, events) => {
  const server = getBunServer<{
    upgrade<T>(
      req: Request,
      options?: {
        data: T
      }
    ): boolean
  }>(c)

  if (!server) {
    throw new TypeError('env has to include the 2nd argument of fetch.')
  }
  const upgradeResult = server.upgrade<BunWebSocketData>(c.req.raw, {
    data: {
      events,
      url: new URL(c.req.url),
      // The first requested subprotocol, exposed via WSContext.protocol to
      // match the deno and cloudflare adapters.
      protocol: c.req.header('sec-websocket-protocol')?.split(',')[0]?.trim() ?? '',
    },
  })
  if (upgradeResult) {
    return new Response(null)
  }
  return // failed
})

export const websocket: BunWebSocketHandler<BunWebSocketData> = {
  open(ws) {

View on GitHub (pinned to e2740d5a1b)

Solutions

  1. Use `Bun.serve({ fetch: app.fetch })` directly so Bun's server reaches c.env
  2. Forward all args in wrappers: `fetch: (...args) => app.fetch(...args)`
  3. Match the adapter to the runtime (use @hono/node-server WebSocket helper on Node)
  4. For tests, run against a real Bun.serve instance or mock c.env.server with an upgrade() method

Example fix

// before
Bun.serve({ fetch: (req, _env) => app.fetch(req, _env) }) // env not a Bun Server

// after
Bun.serve({ fetch: app.fetch, port: 3000 })
Defensive patterns

Strategy: validation

Validate before calling

app.get('/ws', upgradeWebSocket(handler))

// ensure mounting passes the server through:
const server = Bun.serve({ fetch: app.fetch, port: 3000 })

Type guard

const hasUpgradeableEnv = (env: unknown): env is { server: { upgrade(req: Request, opts: object): boolean } } =>
  typeof (env as any)?.server?.upgrade === 'function'

Try / catch

try { return await upgradeWebSocket(handler)(c) } catch (e) { if (e instanceof Error && e.message === 'env has to include the 2nd argument of fetch.') return c.text('WebSocket unavailable', 501); throw e }

Prevention

When it happens

Trigger: Using `upgradeWebSocket`/`createBunWebSocket` handlers on a Hono app that is not the direct fetch handler of Bun.serve — e.g. wrapped fetch that drops the server arg, running under Node, or calling app.request() in tests — then a WebSocket upgrade request arrives.

Common situations: Bun deployment where fetch is wrapped (e.g. for tracing/logging) without forwarding arguments; mixing Node WebSocket adapters with a Bun runtime or vice versa; integration tests that don't spin up a real Bun.serve.

Related errors


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