honojs/hono · error · DetailedError

${_fetchRes.status} ${_fetchRes.statusText}

Error message

${_fetchRes.status} ${_fetchRes.statusText}

What it means

fetchRP is Hono's tiny RPC/fetch client. After optionally consuming the response body, it checks `response.ok` and throws a DetailedError whose message is the HTTP status line (e.g. "404 Not Found"), attaching statusCode, parsed body data, and statusText. This is how non-2xx responses surface to the caller.

Source

Thrown at src/client/fetch-result-please.ts:34

    _data: any
    /**
     * @description BodyInit property from whatwg-fetch polyfill
     *
     * @link https://github.com/JakeChampion/fetch/blob/main/fetch.js#L238
     */
    _bodyInit?: any
  }

  const hasBody =
    (_fetchRes.body || _fetchRes._bodyInit) && !nullBodyResponses.has(_fetchRes.status)

  if (hasBody) {
    const responseType = detectResponseType(_fetchRes)
    _fetchRes._data = await _fetchRes[responseType]()
  }

  if (!_fetchRes.ok) {
    throw new DetailedError(`${_fetchRes.status} ${_fetchRes.statusText}`, {
      statusCode: _fetchRes?.status,
      detail: {
        data: _fetchRes?._data,
        statusText: _fetchRes?.statusText,
      },
    })
  }

  return _fetchRes._data
}

export class DetailedError extends Error {
  /**
   * Additional `message` that will be logged AND returned to client
   */
  public detail?: any
  /**
   * Additional `code` that will be logged AND returned to client

View on GitHub (pinned to e2740d5a1b)

Solutions

  1. Inspect the caught DetailedError: `err.statusCode` and `err.detail.data` usually contain the server's error body — log them to find the real cause
  2. Fix the route/method/URL to match the server-side definition (verify with the typed client or a manual curl)
  3. Add auth headers or middleware that was omitted
  4. Add error-handling middleware (app.onError) on the server so 500s return meaningful JSON
  5. If a proxy is involved, check its logs/config for the failing upstream

Example fix

// before
const res = await client.hello.$get()
const data = await res.json() // throws '404 Not Found' via fetchRP

// after
try {
  const res = await client.hello.$get()
} catch (e) {
  if (e instanceof DetailedError) console.error(e.statusCode, e.detail.data)
  throw e
}
Defensive patterns

Strategy: try-catch

Type guard

import { DetailedError } from 'hono/client/fetch-result-please'
// (or duck-type)
const isDetailedHttpError = (e: unknown): e is { statusCode?: number; detail?: unknown } =>
  typeof (e as any)?.statusCode === 'number'

Try / catch

try {
  const res = await client.posts.$get()
} catch (e) {
  if (typeof (e as any)?.statusCode === 'number') {
    const { statusCode, detail } = e as any
    if (statusCode === 404) /* missing route */
    else if (statusCode >= 500) /* server-side, maybe retry */
    else /* 4xx: inspect detail.data for validation info */
  }
  throw e
}

Prevention

When it happens

Trigger: Any request where the server responds with a 4xx/5xx status: hitting a wrong RPC route/path, missing route method, auth failure (401/403), validation failure (400/422), server error (500), or a proxy returning an error page.

Common situations: RPC client/server route mismatch after refactoring; forgetting to await an error-handling middleware; gateway timeouts behind reverse proxies; calling endpoints requiring auth without credentials; the HTML error pages of dev proxies being returned instead of JSON.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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