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

Hono's Bun adapter getConnInfo() needs access to the Bun `Server` object that served the request. On Bun, that server instance is only available when Hono is mounted via `Bun.serve({ fetch: app.fetch, ... })` — Bun passes the server as the second argument of fetch(), and Hono stores it in `c.env`. If getConnInfo() is called outside that setup, `c.env.server` is undefined and this TypeError is thrown.

Source

Thrown at src/adapter/bun/conninfo.ts:20

import type { GetConnInfo } from '../../helper/conninfo'
import { getBunServer } from './server'

/**
 * Get ConnInfo with Bun
 * @param c Context
 * @returns ConnInfo
 */
export const getConnInfo: GetConnInfo = (c: Context) => {
  const server = getBunServer<{
    requestIP?: (req: Request) => {
      address: string
      family: string
      port: number
    } | null
  }>(c)

  if (!server) {
    throw new TypeError('env has to include the 2nd argument of fetch.')
  }
  if (typeof server.requestIP !== 'function') {
    throw new TypeError('server.requestIP is not a function.')
  }

  // https://bun.sh/docs/runtime/http/server#server-requestip-request
  // Returns null for closed requests or Unix domain sockets.
  const info = server.requestIP(c.req.raw)

  if (!info) {
    return {
      remote: {},
    }
  }

  return {
    remote: {
      address: info.address,

View on GitHub (pinned to e2740d5a1b)

Solutions

  1. Mount the app correctly for Bun: `const server = Bun.serve({ fetch: app.fetch, port: 3000 })` so Bun injects the server into c.env
  2. If you wrap fetch, forward all arguments: `Bun.serve({ fetch: (...args) => app.fetch(...args) })`
  3. Guard before calling: check `getConnInfo` is only used inside a real Bun.serve request, e.g. wrap in try/catch or feature-detect the runtime
  4. In tests, call getConnInfo only against a real Bun.serve instance, not app.request()

Example fix

// before
const app = new Hono()
app.get((c) => c.json({ ip: getConnInfo(c).remote.address }))
// started in a way that drops Bun's server arg
Bun.serve({ fetch: (req) => app.fetch(req) }) // throws

// after
Bun.serve({ fetch: app.fetch, port: 3000 }) // Bun passes (req, server) -> c.env.server
Defensive patterns

Strategy: validation

Validate before calling

import { getConnInfo } from 'hono/bun'

function hasBunServer(c: Context): boolean {
  return Boolean((c.env as any)?.server)
}

app.get('/ip', (c) => {
  if (!hasBunServer(c)) return c.text('ip unavailable', 200)
  return c.json(getConnInfo(c))
})

Type guard

const isBunServerEnv = (
  env: unknown
): env is { server: { requestIP(req: Request): { address: string; family: string; port: number } } } =>
  typeof (env as any)?.server?.requestIP === 'function'

Try / catch

try { const info = getConnInfo(c) } catch (e) { if (e instanceof TypeError && e.message.startsWith('env has to include')) { /* fall back to x-forwarded-for */ } else throw e }

Prevention

When it happens

Trigger: Calling `getConnInfo(c)` from `hono/bun` when the app was not registered as the `fetch` handler of `Bun.serve()` (e.g. using `app.fetch` standalone, running under Node.js/deno, or a custom fetch wrapper that drops Bun's second `server` argument), so `c.env.server` is falsy.

Common situations: Porting a Hono app from Node (via @hono/node-server) to Bun and forgetting to switch to `Bun.serve({ fetch: app.fetch })`; unit tests calling handlers with `app.request()` where no Bun server exists; wrapping app.fetch in a closure like `(req) => app.fetch(req)` which discards the `server` parameter.

Related errors


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