Skip to content

throwOnError throws raw JSON object instead of Error instance, losing HTTP status code #3632

@hungnm-kaopiz

Description

@hungnm-kaopiz

When throwOnError: true is set and the server returns a 4xx/5xx response, the client does not throw an Error instance. Instead, it parses the response body as JSON and throws the resulting plain object directly.

This means:

  • error instanceof Errorfalse
  • error.statusundefined (HTTP status code is lost)
  • The only way to identify the error is by inspecting fields like error.message, which are API-specific and not standardized.

Steps to Reproduce

import { createClient } from '@hey-api/client-fetch';

const client = createClient({ baseUrl: 'https://example.com' });

try {
  await client.get({ url: '/protected', throwOnError: true });
} catch (error) {
  console.log(error instanceof Error);  // ❌ false
  console.log(error.status);            // ❌ undefined — HTTP status lost!
  console.log(error.message);           // ✅ e.g. "auth.unauthenticated.wrong_token"
}

Expected Behavior

The thrown value should be (or extend) an Error instance with the HTTP status code attached, similar to the old ApiError from openapi-typescript-codegen (pre-0.32.x):

catch (error) {
  error instanceof Error   // ✅ true
  error.status             // ✅ 401, 403, 404, 500, ...
  error.body               // ✅ parsed JSON body
}

Impact

This is a silent breaking behavior that is especially problematic when:

  1. Using global error handlers that distinguish errors by HTTP status (e.g. redirect on 401, show 404 page, etc.).
  2. Using frameworks like Next.js — an unrecognized thrown object that is not an Error instance can crash the application with a cryptic Digest error message, because error boundaries and middleware expect Error instances.
  3. Migrating from the legacy client where ApiError with .status was the standard.

Reproduction

https://stackblitz.com/edit/hey-api-client-fetch-example-99xjbds1?file=src%2FApp.tsx


Environment

  • @hey-api/client-fetch: latest

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug 🔥Broken or incorrect behavior.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions