diff --git a/docs/openapi-ts/clients/ky.md b/docs/openapi-ts/clients/ky.md
index e05a487865..098c754fb0 100644
--- a/docs/openapi-ts/clients/ky.md
+++ b/docs/openapi-ts/clients/ky.md
@@ -1,6 +1,6 @@
---
-title: Ky v1 Client
-description: Generate a type-safe Ky v1 client from OpenAPI with the Ky client for openapi-ts. Fully compatible with validators, transformers, and all core features.
+title: Ky Client
+description: Generate a type-safe Ky 2 client from OpenAPI with the Ky client for openapi-ts. Fully compatible with validators, transformers, and all core features.
---
- Ky v1
-
+ Ky v2
+
### About
@@ -23,7 +23,7 @@ The Ky client for Hey API generates a type-safe client from your OpenAPI spec, f
### Collaborators
-
+
## Features
@@ -62,6 +62,22 @@ npx @hey-api/openapi-ts \
The Ky client is built as a thin wrapper on top of Ky, extending its functionality to work with Hey API. If you're already familiar with Ky, configuring your client will feel like working directly with Ky.
+The generated client targets **Ky 2.x** (Ky 2 requires [Node.js 22](https://github.com/sindresorhus/ky/releases/tag/v2.0.0) or newer). Install `ky@^2` in your application.
+
+### Ky options and upgrading from Ky 1
+
+Hey API types omit several Ky options that the wrapper sets itself (for example `method`, `body`, and `prefix`). For any other Ky settings, pass them in **`kyOptions`** or on the top-level config where the generated types allow it.
+
+If you are upgrading from Ky 1, rename **`prefixUrl` to `prefix`** anywhere you pass Ky configuration (including inside `kyOptions`). Ky 2 also adds a standard **`baseUrl`** option for URL resolution; see the [Ky v2.0.0 release notes](https://github.com/sindresorhus/ky/releases/tag/v2.0.0) for hooks, `HTTPError.data`, and other breaking changes.
+
+```js
+client.setConfig({
+ kyOptions: {
+ prefix: 'https://api.example.com/v1/', // was `prefixUrl` in Ky 1
+ },
+});
+```
+
When we installed the client above, it created a [`client.gen.ts`](/openapi-ts/output#client) file. You will most likely want to configure the exported `client` instance. There are two ways to do that.
### `setConfig()`
diff --git a/examples/openapi-ts-ky/package.json b/examples/openapi-ts-ky/package.json
index 0424a4cb7a..8427528620 100644
--- a/examples/openapi-ts-ky/package.json
+++ b/examples/openapi-ts-ky/package.json
@@ -15,7 +15,7 @@
"@radix-ui/react-form": "0.1.1",
"@radix-ui/react-icons": "1.3.2",
"@radix-ui/themes": "3.1.6",
- "ky": "1.14.0",
+ "ky": "2.0.0",
"react": "19.0.0",
"react-dom": "19.0.0"
},
diff --git a/examples/openapi-ts-ky/src/client/client.gen.ts b/examples/openapi-ts-ky/src/client/client.gen.ts
index 84318ba67c..f2fd10a58a 100644
--- a/examples/openapi-ts-ky/src/client/client.gen.ts
+++ b/examples/openapi-ts-ky/src/client/client.gen.ts
@@ -15,6 +15,4 @@ export type CreateClientConfig = (
override?: Config,
) => Config & T>;
-export const client = createClient(
- createConfig({ baseUrl: 'https://petstore3.swagger.io/api/v3' }),
-);
+export const client = createClient(createConfig({ baseUrl: '/api/v3' }));
diff --git a/examples/openapi-ts-ky/src/client/client/client.gen.ts b/examples/openapi-ts-ky/src/client/client/client.gen.ts
index 6626a526bd..e2ad1ddd66 100644
--- a/examples/openapi-ts-ky/src/client/client/client.gen.ts
+++ b/examples/openapi-ts-ky/src/client/client/client.gen.ts
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
-import type { HTTPError, Options as KyOptions } from 'ky';
-import ky from 'ky';
+import type { Options as KyOptions } from 'ky';
+import ky, { isHTTPError } from 'ky';
import { createSseClient } from '../core/serverSentEvents.gen';
import type { HttpMethod } from '../core/types.gen';
@@ -83,22 +83,44 @@ export const createClient = (config: Config = {}): Client => {
request: Request,
opts: ResolvedRequestOptions,
interceptorsMiddleware: Middleware,
+ kyHttpError?: { bodyConsumed: true; data: unknown },
) => {
const result = {
request,
response,
};
- const textError = await response.text();
- let jsonError: unknown;
+ let error: unknown;
- try {
- jsonError = JSON.parse(textError);
- } catch {
- jsonError = undefined;
+ if (kyHttpError) {
+ if (kyHttpError.data !== undefined) {
+ if (typeof kyHttpError.data === 'string') {
+ let jsonError: unknown;
+ try {
+ jsonError = JSON.parse(kyHttpError.data);
+ } catch {
+ jsonError = undefined;
+ }
+ error = jsonError ?? kyHttpError.data;
+ } else {
+ error = kyHttpError.data;
+ }
+ } else {
+ error = '';
+ }
+ } else {
+ const textError = await response.text();
+ let jsonError: unknown;
+
+ try {
+ jsonError = JSON.parse(textError);
+ } catch {
+ jsonError = undefined;
+ }
+
+ error = jsonError ?? textError;
}
- const error = jsonError ?? textError;
let finalError = error;
for (const fn of interceptorsMiddleware.error.fns) {
@@ -174,9 +196,8 @@ export const createClient = (config: Config = {}): Client => {
try {
response = await kyInstance(request, kyOptions);
} catch (error) {
- if (error && typeof error === 'object' && 'response' in error) {
- const httpError = error as HTTPError;
- response = httpError.response;
+ if (isHTTPError(error)) {
+ response = error.response;
for (const fn of interceptors.response.fns) {
if (fn) {
@@ -184,7 +205,21 @@ export const createClient = (config: Config = {}): Client => {
}
}
- return parseErrorResponse(response, request, opts, interceptors);
+ if (error.data !== undefined) {
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: error.data,
+ });
+ }
+
+ if (!response.bodyUsed) {
+ return parseErrorResponse(response, request, opts, interceptors);
+ }
+
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: undefined,
+ });
}
throw error;
diff --git a/examples/openapi-ts-ky/src/client/client/types.gen.ts b/examples/openapi-ts-ky/src/client/client/types.gen.ts
index 40f8e75d49..0460758dac 100644
--- a/examples/openapi-ts-ky/src/client/client/types.gen.ts
+++ b/examples/openapi-ts-ky/src/client/client/types.gen.ts
@@ -33,7 +33,7 @@ export interface RetryOptions {
export interface Config
extends
- Omit,
+ Omit,
CoreConfig {
/**
* Base URL for all requests made by this client.
@@ -48,7 +48,7 @@ export interface Config
* Additional ky-specific options that will be passed directly to ky.
* This allows you to use any ky option not explicitly exposed in the config.
*/
- kyOptions?: Omit;
+ kyOptions?: Omit;
/**
* Return the response data parsed in a specified format. By default, `auto`
* will infer the appropriate method from the `Content-Type` response header.
diff --git a/examples/openapi-ts-ky/src/client/schemas.gen.ts b/examples/openapi-ts-ky/src/client/schemas.gen.ts
index 646632e830..ba5f34b019 100644
--- a/examples/openapi-ts-ky/src/client/schemas.gen.ts
+++ b/examples/openapi-ts-ky/src/client/schemas.gen.ts
@@ -32,7 +32,6 @@ export const OrderSchema = {
},
},
type: 'object',
- 'x-swagger-router-model': 'io.swagger.petstore.model.Order',
xml: {
name: 'order',
},
@@ -51,7 +50,6 @@ export const CategorySchema = {
},
},
type: 'object',
- 'x-swagger-router-model': 'io.swagger.petstore.model.Category',
xml: {
name: 'category',
},
@@ -96,7 +94,6 @@ export const UserSchema = {
},
},
type: 'object',
- 'x-swagger-router-model': 'io.swagger.petstore.model.User',
xml: {
name: 'user',
},
@@ -113,7 +110,6 @@ export const TagSchema = {
},
},
type: 'object',
- 'x-swagger-router-model': 'io.swagger.petstore.model.Tag',
xml: {
name: 'tag',
},
@@ -162,7 +158,6 @@ export const PetSchema = {
},
required: ['name', 'photoUrls'],
type: 'object',
- 'x-swagger-router-model': 'io.swagger.petstore.model.Pet',
xml: {
name: 'pet',
},
diff --git a/examples/openapi-ts-ky/src/client/types.gen.ts b/examples/openapi-ts-ky/src/client/types.gen.ts
index a8ea86379a..9f6e1cd222 100644
--- a/examples/openapi-ts-ky/src/client/types.gen.ts
+++ b/examples/openapi-ts-ky/src/client/types.gen.ts
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
export type ClientOptions = {
- baseUrl: 'https://petstore3.swagger.io/api/v3' | (string & {});
+ baseUrl: `${string}://${string}/api/v3` | (string & {});
};
export type Order = {
diff --git a/packages/openapi-ts-tests/main/package.json b/packages/openapi-ts-tests/main/package.json
index 48a8e129c8..331a397ac2 100644
--- a/packages/openapi-ts-tests/main/package.json
+++ b/packages/openapi-ts-tests/main/package.json
@@ -35,7 +35,7 @@
"cross-spawn": "7.0.6",
"eslint": "9.39.2",
"fastify": "5.7.4",
- "ky": "1.14.3",
+ "ky": "2.0.0",
"node-fetch": "3.3.2",
"nuxt": "3.14.1592",
"ofetch": "1.5.1",
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-false/client/client.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-false/client/client.gen.ts
index bc973c7147..06b6948b64 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-false/client/client.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-false/client/client.gen.ts
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
-import type { HTTPError, Options as KyOptions } from 'ky';
-import ky from 'ky';
+import type { Options as KyOptions } from 'ky';
+import ky, { isHTTPError } from 'ky';
import { createSseClient } from '../core/serverSentEvents.gen';
import type { HttpMethod } from '../core/types.gen';
@@ -77,22 +77,44 @@ export const createClient = (config: Config = {}): Client => {
request: Request,
opts: ResolvedRequestOptions,
interceptorsMiddleware: Middleware,
+ kyHttpError?: { bodyConsumed: true; data: unknown },
) => {
const result = {
request,
response,
};
- const textError = await response.text();
- let jsonError: unknown;
+ let error: unknown;
- try {
- jsonError = JSON.parse(textError);
- } catch {
- jsonError = undefined;
+ if (kyHttpError) {
+ if (kyHttpError.data !== undefined) {
+ if (typeof kyHttpError.data === 'string') {
+ let jsonError: unknown;
+ try {
+ jsonError = JSON.parse(kyHttpError.data);
+ } catch {
+ jsonError = undefined;
+ }
+ error = jsonError ?? kyHttpError.data;
+ } else {
+ error = kyHttpError.data;
+ }
+ } else {
+ error = '';
+ }
+ } else {
+ const textError = await response.text();
+ let jsonError: unknown;
+
+ try {
+ jsonError = JSON.parse(textError);
+ } catch {
+ jsonError = undefined;
+ }
+
+ error = jsonError ?? textError;
}
- const error = jsonError ?? textError;
let finalError = error;
for (const fn of interceptorsMiddleware.error.fns) {
@@ -168,9 +190,8 @@ export const createClient = (config: Config = {}): Client => {
try {
response = await kyInstance(request, kyOptions);
} catch (error) {
- if (error && typeof error === 'object' && 'response' in error) {
- const httpError = error as HTTPError;
- response = httpError.response;
+ if (isHTTPError(error)) {
+ response = error.response;
for (const fn of interceptors.response.fns) {
if (fn) {
@@ -178,7 +199,21 @@ export const createClient = (config: Config = {}): Client => {
}
}
- return parseErrorResponse(response, request, opts, interceptors);
+ if (error.data !== undefined) {
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: error.data,
+ });
+ }
+
+ if (!response.bodyUsed) {
+ return parseErrorResponse(response, request, opts, interceptors);
+ }
+
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: undefined,
+ });
}
throw error;
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-false/client/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-false/client/types.gen.ts
index e2bf520a96..dbaf110251 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-false/client/types.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-false/client/types.gen.ts
@@ -36,7 +36,7 @@ export interface RetryOptions {
export interface Config
extends
- Omit,
+ Omit,
CoreConfig {
/**
* Base URL for all requests made by this client.
@@ -51,7 +51,7 @@ export interface Config
* Additional ky-specific options that will be passed directly to ky.
* This allows you to use any ky option not explicitly exposed in the config.
*/
- kyOptions?: Omit;
+ kyOptions?: Omit;
/**
* Return the response data parsed in a specified format. By default, `auto`
* will infer the appropriate method from the `Content-Type` response header.
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-number/client/client.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-number/client/client.gen.ts
index bc973c7147..06b6948b64 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-number/client/client.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-number/client/client.gen.ts
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
-import type { HTTPError, Options as KyOptions } from 'ky';
-import ky from 'ky';
+import type { Options as KyOptions } from 'ky';
+import ky, { isHTTPError } from 'ky';
import { createSseClient } from '../core/serverSentEvents.gen';
import type { HttpMethod } from '../core/types.gen';
@@ -77,22 +77,44 @@ export const createClient = (config: Config = {}): Client => {
request: Request,
opts: ResolvedRequestOptions,
interceptorsMiddleware: Middleware,
+ kyHttpError?: { bodyConsumed: true; data: unknown },
) => {
const result = {
request,
response,
};
- const textError = await response.text();
- let jsonError: unknown;
+ let error: unknown;
- try {
- jsonError = JSON.parse(textError);
- } catch {
- jsonError = undefined;
+ if (kyHttpError) {
+ if (kyHttpError.data !== undefined) {
+ if (typeof kyHttpError.data === 'string') {
+ let jsonError: unknown;
+ try {
+ jsonError = JSON.parse(kyHttpError.data);
+ } catch {
+ jsonError = undefined;
+ }
+ error = jsonError ?? kyHttpError.data;
+ } else {
+ error = kyHttpError.data;
+ }
+ } else {
+ error = '';
+ }
+ } else {
+ const textError = await response.text();
+ let jsonError: unknown;
+
+ try {
+ jsonError = JSON.parse(textError);
+ } catch {
+ jsonError = undefined;
+ }
+
+ error = jsonError ?? textError;
}
- const error = jsonError ?? textError;
let finalError = error;
for (const fn of interceptorsMiddleware.error.fns) {
@@ -168,9 +190,8 @@ export const createClient = (config: Config = {}): Client => {
try {
response = await kyInstance(request, kyOptions);
} catch (error) {
- if (error && typeof error === 'object' && 'response' in error) {
- const httpError = error as HTTPError;
- response = httpError.response;
+ if (isHTTPError(error)) {
+ response = error.response;
for (const fn of interceptors.response.fns) {
if (fn) {
@@ -178,7 +199,21 @@ export const createClient = (config: Config = {}): Client => {
}
}
- return parseErrorResponse(response, request, opts, interceptors);
+ if (error.data !== undefined) {
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: error.data,
+ });
+ }
+
+ if (!response.bodyUsed) {
+ return parseErrorResponse(response, request, opts, interceptors);
+ }
+
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: undefined,
+ });
}
throw error;
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-number/client/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-number/client/types.gen.ts
index e2bf520a96..dbaf110251 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-number/client/types.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-number/client/types.gen.ts
@@ -36,7 +36,7 @@ export interface RetryOptions {
export interface Config
extends
- Omit,
+ Omit,
CoreConfig {
/**
* Base URL for all requests made by this client.
@@ -51,7 +51,7 @@ export interface Config
* Additional ky-specific options that will be passed directly to ky.
* This allows you to use any ky option not explicitly exposed in the config.
*/
- kyOptions?: Omit;
+ kyOptions?: Omit;
/**
* Return the response data parsed in a specified format. By default, `auto`
* will infer the appropriate method from the `Content-Type` response header.
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-strict/client/client.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-strict/client/client.gen.ts
index bc973c7147..06b6948b64 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-strict/client/client.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-strict/client/client.gen.ts
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
-import type { HTTPError, Options as KyOptions } from 'ky';
-import ky from 'ky';
+import type { Options as KyOptions } from 'ky';
+import ky, { isHTTPError } from 'ky';
import { createSseClient } from '../core/serverSentEvents.gen';
import type { HttpMethod } from '../core/types.gen';
@@ -77,22 +77,44 @@ export const createClient = (config: Config = {}): Client => {
request: Request,
opts: ResolvedRequestOptions,
interceptorsMiddleware: Middleware,
+ kyHttpError?: { bodyConsumed: true; data: unknown },
) => {
const result = {
request,
response,
};
- const textError = await response.text();
- let jsonError: unknown;
+ let error: unknown;
- try {
- jsonError = JSON.parse(textError);
- } catch {
- jsonError = undefined;
+ if (kyHttpError) {
+ if (kyHttpError.data !== undefined) {
+ if (typeof kyHttpError.data === 'string') {
+ let jsonError: unknown;
+ try {
+ jsonError = JSON.parse(kyHttpError.data);
+ } catch {
+ jsonError = undefined;
+ }
+ error = jsonError ?? kyHttpError.data;
+ } else {
+ error = kyHttpError.data;
+ }
+ } else {
+ error = '';
+ }
+ } else {
+ const textError = await response.text();
+ let jsonError: unknown;
+
+ try {
+ jsonError = JSON.parse(textError);
+ } catch {
+ jsonError = undefined;
+ }
+
+ error = jsonError ?? textError;
}
- const error = jsonError ?? textError;
let finalError = error;
for (const fn of interceptorsMiddleware.error.fns) {
@@ -168,9 +190,8 @@ export const createClient = (config: Config = {}): Client => {
try {
response = await kyInstance(request, kyOptions);
} catch (error) {
- if (error && typeof error === 'object' && 'response' in error) {
- const httpError = error as HTTPError;
- response = httpError.response;
+ if (isHTTPError(error)) {
+ response = error.response;
for (const fn of interceptors.response.fns) {
if (fn) {
@@ -178,7 +199,21 @@ export const createClient = (config: Config = {}): Client => {
}
}
- return parseErrorResponse(response, request, opts, interceptors);
+ if (error.data !== undefined) {
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: error.data,
+ });
+ }
+
+ if (!response.bodyUsed) {
+ return parseErrorResponse(response, request, opts, interceptors);
+ }
+
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: undefined,
+ });
}
throw error;
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-strict/client/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-strict/client/types.gen.ts
index e2bf520a96..dbaf110251 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-strict/client/types.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-strict/client/types.gen.ts
@@ -36,7 +36,7 @@ export interface RetryOptions {
export interface Config
extends
- Omit,
+ Omit,
CoreConfig {
/**
* Base URL for all requests made by this client.
@@ -51,7 +51,7 @@ export interface Config
* Additional ky-specific options that will be passed directly to ky.
* This allows you to use any ky option not explicitly exposed in the config.
*/
- kyOptions?: Omit;
+ kyOptions?: Omit;
/**
* Return the response data parsed in a specified format. By default, `auto`
* will infer the appropriate method from the `Content-Type` response header.
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-string/client/client.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-string/client/client.gen.ts
index bc973c7147..06b6948b64 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-string/client/client.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-string/client/client.gen.ts
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
-import type { HTTPError, Options as KyOptions } from 'ky';
-import ky from 'ky';
+import type { Options as KyOptions } from 'ky';
+import ky, { isHTTPError } from 'ky';
import { createSseClient } from '../core/serverSentEvents.gen';
import type { HttpMethod } from '../core/types.gen';
@@ -77,22 +77,44 @@ export const createClient = (config: Config = {}): Client => {
request: Request,
opts: ResolvedRequestOptions,
interceptorsMiddleware: Middleware,
+ kyHttpError?: { bodyConsumed: true; data: unknown },
) => {
const result = {
request,
response,
};
- const textError = await response.text();
- let jsonError: unknown;
+ let error: unknown;
- try {
- jsonError = JSON.parse(textError);
- } catch {
- jsonError = undefined;
+ if (kyHttpError) {
+ if (kyHttpError.data !== undefined) {
+ if (typeof kyHttpError.data === 'string') {
+ let jsonError: unknown;
+ try {
+ jsonError = JSON.parse(kyHttpError.data);
+ } catch {
+ jsonError = undefined;
+ }
+ error = jsonError ?? kyHttpError.data;
+ } else {
+ error = kyHttpError.data;
+ }
+ } else {
+ error = '';
+ }
+ } else {
+ const textError = await response.text();
+ let jsonError: unknown;
+
+ try {
+ jsonError = JSON.parse(textError);
+ } catch {
+ jsonError = undefined;
+ }
+
+ error = jsonError ?? textError;
}
- const error = jsonError ?? textError;
let finalError = error;
for (const fn of interceptorsMiddleware.error.fns) {
@@ -168,9 +190,8 @@ export const createClient = (config: Config = {}): Client => {
try {
response = await kyInstance(request, kyOptions);
} catch (error) {
- if (error && typeof error === 'object' && 'response' in error) {
- const httpError = error as HTTPError;
- response = httpError.response;
+ if (isHTTPError(error)) {
+ response = error.response;
for (const fn of interceptors.response.fns) {
if (fn) {
@@ -178,7 +199,21 @@ export const createClient = (config: Config = {}): Client => {
}
}
- return parseErrorResponse(response, request, opts, interceptors);
+ if (error.data !== undefined) {
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: error.data,
+ });
+ }
+
+ if (!response.bodyUsed) {
+ return parseErrorResponse(response, request, opts, interceptors);
+ }
+
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: undefined,
+ });
}
throw error;
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-string/client/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-string/client/types.gen.ts
index e2bf520a96..dbaf110251 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-string/client/types.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/base-url-string/client/types.gen.ts
@@ -36,7 +36,7 @@ export interface RetryOptions {
export interface Config
extends
- Omit,
+ Omit,
CoreConfig {
/**
* Base URL for all requests made by this client.
@@ -51,7 +51,7 @@ export interface Config
* Additional ky-specific options that will be passed directly to ky.
* This allows you to use any ky option not explicitly exposed in the config.
*/
- kyOptions?: Omit;
+ kyOptions?: Omit;
/**
* Return the response data parsed in a specified format. By default, `auto`
* will infer the appropriate method from the `Content-Type` response header.
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/clean-false/client/client.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/clean-false/client/client.gen.ts
index bc973c7147..06b6948b64 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/clean-false/client/client.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/clean-false/client/client.gen.ts
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
-import type { HTTPError, Options as KyOptions } from 'ky';
-import ky from 'ky';
+import type { Options as KyOptions } from 'ky';
+import ky, { isHTTPError } from 'ky';
import { createSseClient } from '../core/serverSentEvents.gen';
import type { HttpMethod } from '../core/types.gen';
@@ -77,22 +77,44 @@ export const createClient = (config: Config = {}): Client => {
request: Request,
opts: ResolvedRequestOptions,
interceptorsMiddleware: Middleware,
+ kyHttpError?: { bodyConsumed: true; data: unknown },
) => {
const result = {
request,
response,
};
- const textError = await response.text();
- let jsonError: unknown;
+ let error: unknown;
- try {
- jsonError = JSON.parse(textError);
- } catch {
- jsonError = undefined;
+ if (kyHttpError) {
+ if (kyHttpError.data !== undefined) {
+ if (typeof kyHttpError.data === 'string') {
+ let jsonError: unknown;
+ try {
+ jsonError = JSON.parse(kyHttpError.data);
+ } catch {
+ jsonError = undefined;
+ }
+ error = jsonError ?? kyHttpError.data;
+ } else {
+ error = kyHttpError.data;
+ }
+ } else {
+ error = '';
+ }
+ } else {
+ const textError = await response.text();
+ let jsonError: unknown;
+
+ try {
+ jsonError = JSON.parse(textError);
+ } catch {
+ jsonError = undefined;
+ }
+
+ error = jsonError ?? textError;
}
- const error = jsonError ?? textError;
let finalError = error;
for (const fn of interceptorsMiddleware.error.fns) {
@@ -168,9 +190,8 @@ export const createClient = (config: Config = {}): Client => {
try {
response = await kyInstance(request, kyOptions);
} catch (error) {
- if (error && typeof error === 'object' && 'response' in error) {
- const httpError = error as HTTPError;
- response = httpError.response;
+ if (isHTTPError(error)) {
+ response = error.response;
for (const fn of interceptors.response.fns) {
if (fn) {
@@ -178,7 +199,21 @@ export const createClient = (config: Config = {}): Client => {
}
}
- return parseErrorResponse(response, request, opts, interceptors);
+ if (error.data !== undefined) {
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: error.data,
+ });
+ }
+
+ if (!response.bodyUsed) {
+ return parseErrorResponse(response, request, opts, interceptors);
+ }
+
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: undefined,
+ });
}
throw error;
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/clean-false/client/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/clean-false/client/types.gen.ts
index e2bf520a96..dbaf110251 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/clean-false/client/types.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/clean-false/client/types.gen.ts
@@ -36,7 +36,7 @@ export interface RetryOptions {
export interface Config
extends
- Omit,
+ Omit,
CoreConfig {
/**
* Base URL for all requests made by this client.
@@ -51,7 +51,7 @@ export interface Config
* Additional ky-specific options that will be passed directly to ky.
* This allows you to use any ky option not explicitly exposed in the config.
*/
- kyOptions?: Omit;
+ kyOptions?: Omit;
/**
* Return the response data parsed in a specified format. By default, `auto`
* will infer the appropriate method from the `Content-Type` response header.
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/default/client/client.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/default/client/client.gen.ts
index bc973c7147..06b6948b64 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/default/client/client.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/default/client/client.gen.ts
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
-import type { HTTPError, Options as KyOptions } from 'ky';
-import ky from 'ky';
+import type { Options as KyOptions } from 'ky';
+import ky, { isHTTPError } from 'ky';
import { createSseClient } from '../core/serverSentEvents.gen';
import type { HttpMethod } from '../core/types.gen';
@@ -77,22 +77,44 @@ export const createClient = (config: Config = {}): Client => {
request: Request,
opts: ResolvedRequestOptions,
interceptorsMiddleware: Middleware,
+ kyHttpError?: { bodyConsumed: true; data: unknown },
) => {
const result = {
request,
response,
};
- const textError = await response.text();
- let jsonError: unknown;
+ let error: unknown;
- try {
- jsonError = JSON.parse(textError);
- } catch {
- jsonError = undefined;
+ if (kyHttpError) {
+ if (kyHttpError.data !== undefined) {
+ if (typeof kyHttpError.data === 'string') {
+ let jsonError: unknown;
+ try {
+ jsonError = JSON.parse(kyHttpError.data);
+ } catch {
+ jsonError = undefined;
+ }
+ error = jsonError ?? kyHttpError.data;
+ } else {
+ error = kyHttpError.data;
+ }
+ } else {
+ error = '';
+ }
+ } else {
+ const textError = await response.text();
+ let jsonError: unknown;
+
+ try {
+ jsonError = JSON.parse(textError);
+ } catch {
+ jsonError = undefined;
+ }
+
+ error = jsonError ?? textError;
}
- const error = jsonError ?? textError;
let finalError = error;
for (const fn of interceptorsMiddleware.error.fns) {
@@ -168,9 +190,8 @@ export const createClient = (config: Config = {}): Client => {
try {
response = await kyInstance(request, kyOptions);
} catch (error) {
- if (error && typeof error === 'object' && 'response' in error) {
- const httpError = error as HTTPError;
- response = httpError.response;
+ if (isHTTPError(error)) {
+ response = error.response;
for (const fn of interceptors.response.fns) {
if (fn) {
@@ -178,7 +199,21 @@ export const createClient = (config: Config = {}): Client => {
}
}
- return parseErrorResponse(response, request, opts, interceptors);
+ if (error.data !== undefined) {
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: error.data,
+ });
+ }
+
+ if (!response.bodyUsed) {
+ return parseErrorResponse(response, request, opts, interceptors);
+ }
+
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: undefined,
+ });
}
throw error;
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/default/client/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/default/client/types.gen.ts
index e2bf520a96..dbaf110251 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/default/client/types.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/default/client/types.gen.ts
@@ -36,7 +36,7 @@ export interface RetryOptions {
export interface Config
extends
- Omit,
+ Omit,
CoreConfig {
/**
* Base URL for all requests made by this client.
@@ -51,7 +51,7 @@ export interface Config
* Additional ky-specific options that will be passed directly to ky.
* This allows you to use any ky option not explicitly exposed in the config.
*/
- kyOptions?: Omit;
+ kyOptions?: Omit;
/**
* Return the response data parsed in a specified format. By default, `auto`
* will infer the appropriate method from the `Content-Type` response header.
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/import-file-extension-ts/client/client.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/import-file-extension-ts/client/client.gen.ts
index 1d351001e7..94d0f289ab 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/import-file-extension-ts/client/client.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/import-file-extension-ts/client/client.gen.ts
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
-import type { HTTPError, Options as KyOptions } from 'ky';
-import ky from 'ky';
+import type { Options as KyOptions } from 'ky';
+import ky, { isHTTPError } from 'ky';
import { createSseClient } from '../core/serverSentEvents.gen.ts';
import type { HttpMethod } from '../core/types.gen.ts';
@@ -77,22 +77,44 @@ export const createClient = (config: Config = {}): Client => {
request: Request,
opts: ResolvedRequestOptions,
interceptorsMiddleware: Middleware,
+ kyHttpError?: { bodyConsumed: true; data: unknown },
) => {
const result = {
request,
response,
};
- const textError = await response.text();
- let jsonError: unknown;
+ let error: unknown;
- try {
- jsonError = JSON.parse(textError);
- } catch {
- jsonError = undefined;
+ if (kyHttpError) {
+ if (kyHttpError.data !== undefined) {
+ if (typeof kyHttpError.data === 'string') {
+ let jsonError: unknown;
+ try {
+ jsonError = JSON.parse(kyHttpError.data);
+ } catch {
+ jsonError = undefined;
+ }
+ error = jsonError ?? kyHttpError.data;
+ } else {
+ error = kyHttpError.data;
+ }
+ } else {
+ error = '';
+ }
+ } else {
+ const textError = await response.text();
+ let jsonError: unknown;
+
+ try {
+ jsonError = JSON.parse(textError);
+ } catch {
+ jsonError = undefined;
+ }
+
+ error = jsonError ?? textError;
}
- const error = jsonError ?? textError;
let finalError = error;
for (const fn of interceptorsMiddleware.error.fns) {
@@ -168,9 +190,8 @@ export const createClient = (config: Config = {}): Client => {
try {
response = await kyInstance(request, kyOptions);
} catch (error) {
- if (error && typeof error === 'object' && 'response' in error) {
- const httpError = error as HTTPError;
- response = httpError.response;
+ if (isHTTPError(error)) {
+ response = error.response;
for (const fn of interceptors.response.fns) {
if (fn) {
@@ -178,7 +199,21 @@ export const createClient = (config: Config = {}): Client => {
}
}
- return parseErrorResponse(response, request, opts, interceptors);
+ if (error.data !== undefined) {
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: error.data,
+ });
+ }
+
+ if (!response.bodyUsed) {
+ return parseErrorResponse(response, request, opts, interceptors);
+ }
+
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: undefined,
+ });
}
throw error;
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/import-file-extension-ts/client/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/import-file-extension-ts/client/types.gen.ts
index b708216861..2573058185 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/import-file-extension-ts/client/types.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/import-file-extension-ts/client/types.gen.ts
@@ -36,7 +36,7 @@ export interface RetryOptions {
export interface Config
extends
- Omit,
+ Omit,
CoreConfig {
/**
* Base URL for all requests made by this client.
@@ -51,7 +51,7 @@ export interface Config
* Additional ky-specific options that will be passed directly to ky.
* This allows you to use any ky option not explicitly exposed in the config.
*/
- kyOptions?: Omit;
+ kyOptions?: Omit;
/**
* Return the response data parsed in a specified format. By default, `auto`
* will infer the appropriate method from the `Content-Type` response header.
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/sdk-client-optional/client/client.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/sdk-client-optional/client/client.gen.ts
index bc973c7147..06b6948b64 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/sdk-client-optional/client/client.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/sdk-client-optional/client/client.gen.ts
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
-import type { HTTPError, Options as KyOptions } from 'ky';
-import ky from 'ky';
+import type { Options as KyOptions } from 'ky';
+import ky, { isHTTPError } from 'ky';
import { createSseClient } from '../core/serverSentEvents.gen';
import type { HttpMethod } from '../core/types.gen';
@@ -77,22 +77,44 @@ export const createClient = (config: Config = {}): Client => {
request: Request,
opts: ResolvedRequestOptions,
interceptorsMiddleware: Middleware,
+ kyHttpError?: { bodyConsumed: true; data: unknown },
) => {
const result = {
request,
response,
};
- const textError = await response.text();
- let jsonError: unknown;
+ let error: unknown;
- try {
- jsonError = JSON.parse(textError);
- } catch {
- jsonError = undefined;
+ if (kyHttpError) {
+ if (kyHttpError.data !== undefined) {
+ if (typeof kyHttpError.data === 'string') {
+ let jsonError: unknown;
+ try {
+ jsonError = JSON.parse(kyHttpError.data);
+ } catch {
+ jsonError = undefined;
+ }
+ error = jsonError ?? kyHttpError.data;
+ } else {
+ error = kyHttpError.data;
+ }
+ } else {
+ error = '';
+ }
+ } else {
+ const textError = await response.text();
+ let jsonError: unknown;
+
+ try {
+ jsonError = JSON.parse(textError);
+ } catch {
+ jsonError = undefined;
+ }
+
+ error = jsonError ?? textError;
}
- const error = jsonError ?? textError;
let finalError = error;
for (const fn of interceptorsMiddleware.error.fns) {
@@ -168,9 +190,8 @@ export const createClient = (config: Config = {}): Client => {
try {
response = await kyInstance(request, kyOptions);
} catch (error) {
- if (error && typeof error === 'object' && 'response' in error) {
- const httpError = error as HTTPError;
- response = httpError.response;
+ if (isHTTPError(error)) {
+ response = error.response;
for (const fn of interceptors.response.fns) {
if (fn) {
@@ -178,7 +199,21 @@ export const createClient = (config: Config = {}): Client => {
}
}
- return parseErrorResponse(response, request, opts, interceptors);
+ if (error.data !== undefined) {
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: error.data,
+ });
+ }
+
+ if (!response.bodyUsed) {
+ return parseErrorResponse(response, request, opts, interceptors);
+ }
+
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: undefined,
+ });
}
throw error;
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/sdk-client-optional/client/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/sdk-client-optional/client/types.gen.ts
index e2bf520a96..dbaf110251 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/sdk-client-optional/client/types.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/sdk-client-optional/client/types.gen.ts
@@ -36,7 +36,7 @@ export interface RetryOptions {
export interface Config
extends
- Omit,
+ Omit,
CoreConfig {
/**
* Base URL for all requests made by this client.
@@ -51,7 +51,7 @@ export interface Config
* Additional ky-specific options that will be passed directly to ky.
* This allows you to use any ky option not explicitly exposed in the config.
*/
- kyOptions?: Omit;
+ kyOptions?: Omit;
/**
* Return the response data parsed in a specified format. By default, `auto`
* will infer the appropriate method from the `Content-Type` response header.
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/sdk-client-required/client/client.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/sdk-client-required/client/client.gen.ts
index bc973c7147..06b6948b64 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/sdk-client-required/client/client.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/sdk-client-required/client/client.gen.ts
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
-import type { HTTPError, Options as KyOptions } from 'ky';
-import ky from 'ky';
+import type { Options as KyOptions } from 'ky';
+import ky, { isHTTPError } from 'ky';
import { createSseClient } from '../core/serverSentEvents.gen';
import type { HttpMethod } from '../core/types.gen';
@@ -77,22 +77,44 @@ export const createClient = (config: Config = {}): Client => {
request: Request,
opts: ResolvedRequestOptions,
interceptorsMiddleware: Middleware,
+ kyHttpError?: { bodyConsumed: true; data: unknown },
) => {
const result = {
request,
response,
};
- const textError = await response.text();
- let jsonError: unknown;
+ let error: unknown;
- try {
- jsonError = JSON.parse(textError);
- } catch {
- jsonError = undefined;
+ if (kyHttpError) {
+ if (kyHttpError.data !== undefined) {
+ if (typeof kyHttpError.data === 'string') {
+ let jsonError: unknown;
+ try {
+ jsonError = JSON.parse(kyHttpError.data);
+ } catch {
+ jsonError = undefined;
+ }
+ error = jsonError ?? kyHttpError.data;
+ } else {
+ error = kyHttpError.data;
+ }
+ } else {
+ error = '';
+ }
+ } else {
+ const textError = await response.text();
+ let jsonError: unknown;
+
+ try {
+ jsonError = JSON.parse(textError);
+ } catch {
+ jsonError = undefined;
+ }
+
+ error = jsonError ?? textError;
}
- const error = jsonError ?? textError;
let finalError = error;
for (const fn of interceptorsMiddleware.error.fns) {
@@ -168,9 +190,8 @@ export const createClient = (config: Config = {}): Client => {
try {
response = await kyInstance(request, kyOptions);
} catch (error) {
- if (error && typeof error === 'object' && 'response' in error) {
- const httpError = error as HTTPError;
- response = httpError.response;
+ if (isHTTPError(error)) {
+ response = error.response;
for (const fn of interceptors.response.fns) {
if (fn) {
@@ -178,7 +199,21 @@ export const createClient = (config: Config = {}): Client => {
}
}
- return parseErrorResponse(response, request, opts, interceptors);
+ if (error.data !== undefined) {
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: error.data,
+ });
+ }
+
+ if (!response.bodyUsed) {
+ return parseErrorResponse(response, request, opts, interceptors);
+ }
+
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: undefined,
+ });
}
throw error;
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/sdk-client-required/client/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/sdk-client-required/client/types.gen.ts
index e2bf520a96..dbaf110251 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/sdk-client-required/client/types.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/sdk-client-required/client/types.gen.ts
@@ -36,7 +36,7 @@ export interface RetryOptions {
export interface Config
extends
- Omit,
+ Omit,
CoreConfig {
/**
* Base URL for all requests made by this client.
@@ -51,7 +51,7 @@ export interface Config
* Additional ky-specific options that will be passed directly to ky.
* This allows you to use any ky option not explicitly exposed in the config.
*/
- kyOptions?: Omit;
+ kyOptions?: Omit;
/**
* Return the response data parsed in a specified format. By default, `auto`
* will infer the appropriate method from the `Content-Type` response header.
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/tsconfig-node16-sdk/client/client.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/tsconfig-node16-sdk/client/client.gen.ts
index ddab114f4d..11828080c1 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/tsconfig-node16-sdk/client/client.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/tsconfig-node16-sdk/client/client.gen.ts
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
-import type { HTTPError, Options as KyOptions } from 'ky';
-import ky from 'ky';
+import type { Options as KyOptions } from 'ky';
+import ky, { isHTTPError } from 'ky';
import { createSseClient } from '../core/serverSentEvents.gen.js';
import type { HttpMethod } from '../core/types.gen.js';
@@ -77,22 +77,44 @@ export const createClient = (config: Config = {}): Client => {
request: Request,
opts: ResolvedRequestOptions,
interceptorsMiddleware: Middleware,
+ kyHttpError?: { bodyConsumed: true; data: unknown },
) => {
const result = {
request,
response,
};
- const textError = await response.text();
- let jsonError: unknown;
+ let error: unknown;
- try {
- jsonError = JSON.parse(textError);
- } catch {
- jsonError = undefined;
+ if (kyHttpError) {
+ if (kyHttpError.data !== undefined) {
+ if (typeof kyHttpError.data === 'string') {
+ let jsonError: unknown;
+ try {
+ jsonError = JSON.parse(kyHttpError.data);
+ } catch {
+ jsonError = undefined;
+ }
+ error = jsonError ?? kyHttpError.data;
+ } else {
+ error = kyHttpError.data;
+ }
+ } else {
+ error = '';
+ }
+ } else {
+ const textError = await response.text();
+ let jsonError: unknown;
+
+ try {
+ jsonError = JSON.parse(textError);
+ } catch {
+ jsonError = undefined;
+ }
+
+ error = jsonError ?? textError;
}
- const error = jsonError ?? textError;
let finalError = error;
for (const fn of interceptorsMiddleware.error.fns) {
@@ -168,9 +190,8 @@ export const createClient = (config: Config = {}): Client => {
try {
response = await kyInstance(request, kyOptions);
} catch (error) {
- if (error && typeof error === 'object' && 'response' in error) {
- const httpError = error as HTTPError;
- response = httpError.response;
+ if (isHTTPError(error)) {
+ response = error.response;
for (const fn of interceptors.response.fns) {
if (fn) {
@@ -178,7 +199,21 @@ export const createClient = (config: Config = {}): Client => {
}
}
- return parseErrorResponse(response, request, opts, interceptors);
+ if (error.data !== undefined) {
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: error.data,
+ });
+ }
+
+ if (!response.bodyUsed) {
+ return parseErrorResponse(response, request, opts, interceptors);
+ }
+
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: undefined,
+ });
}
throw error;
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/tsconfig-node16-sdk/client/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/tsconfig-node16-sdk/client/types.gen.ts
index e87810ac3d..2082e30cb4 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/tsconfig-node16-sdk/client/types.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/tsconfig-node16-sdk/client/types.gen.ts
@@ -36,7 +36,7 @@ export interface RetryOptions {
export interface Config
extends
- Omit,
+ Omit,
CoreConfig {
/**
* Base URL for all requests made by this client.
@@ -51,7 +51,7 @@ export interface Config
* Additional ky-specific options that will be passed directly to ky.
* This allows you to use any ky option not explicitly exposed in the config.
*/
- kyOptions?: Omit;
+ kyOptions?: Omit;
/**
* Return the response data parsed in a specified format. By default, `auto`
* will infer the appropriate method from the `Content-Type` response header.
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/tsconfig-nodenext-sdk/client/client.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/tsconfig-nodenext-sdk/client/client.gen.ts
index ddab114f4d..11828080c1 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/tsconfig-nodenext-sdk/client/client.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/tsconfig-nodenext-sdk/client/client.gen.ts
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
-import type { HTTPError, Options as KyOptions } from 'ky';
-import ky from 'ky';
+import type { Options as KyOptions } from 'ky';
+import ky, { isHTTPError } from 'ky';
import { createSseClient } from '../core/serverSentEvents.gen.js';
import type { HttpMethod } from '../core/types.gen.js';
@@ -77,22 +77,44 @@ export const createClient = (config: Config = {}): Client => {
request: Request,
opts: ResolvedRequestOptions,
interceptorsMiddleware: Middleware,
+ kyHttpError?: { bodyConsumed: true; data: unknown },
) => {
const result = {
request,
response,
};
- const textError = await response.text();
- let jsonError: unknown;
+ let error: unknown;
- try {
- jsonError = JSON.parse(textError);
- } catch {
- jsonError = undefined;
+ if (kyHttpError) {
+ if (kyHttpError.data !== undefined) {
+ if (typeof kyHttpError.data === 'string') {
+ let jsonError: unknown;
+ try {
+ jsonError = JSON.parse(kyHttpError.data);
+ } catch {
+ jsonError = undefined;
+ }
+ error = jsonError ?? kyHttpError.data;
+ } else {
+ error = kyHttpError.data;
+ }
+ } else {
+ error = '';
+ }
+ } else {
+ const textError = await response.text();
+ let jsonError: unknown;
+
+ try {
+ jsonError = JSON.parse(textError);
+ } catch {
+ jsonError = undefined;
+ }
+
+ error = jsonError ?? textError;
}
- const error = jsonError ?? textError;
let finalError = error;
for (const fn of interceptorsMiddleware.error.fns) {
@@ -168,9 +190,8 @@ export const createClient = (config: Config = {}): Client => {
try {
response = await kyInstance(request, kyOptions);
} catch (error) {
- if (error && typeof error === 'object' && 'response' in error) {
- const httpError = error as HTTPError;
- response = httpError.response;
+ if (isHTTPError(error)) {
+ response = error.response;
for (const fn of interceptors.response.fns) {
if (fn) {
@@ -178,7 +199,21 @@ export const createClient = (config: Config = {}): Client => {
}
}
- return parseErrorResponse(response, request, opts, interceptors);
+ if (error.data !== undefined) {
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: error.data,
+ });
+ }
+
+ if (!response.bodyUsed) {
+ return parseErrorResponse(response, request, opts, interceptors);
+ }
+
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: undefined,
+ });
}
throw error;
diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/tsconfig-nodenext-sdk/client/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/tsconfig-nodenext-sdk/client/types.gen.ts
index e87810ac3d..2082e30cb4 100644
--- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/tsconfig-nodenext-sdk/client/types.gen.ts
+++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ky/tsconfig-nodenext-sdk/client/types.gen.ts
@@ -36,7 +36,7 @@ export interface RetryOptions {
export interface Config
extends
- Omit,
+ Omit,
CoreConfig {
/**
* Base URL for all requests made by this client.
@@ -51,7 +51,7 @@ export interface Config
* Additional ky-specific options that will be passed directly to ky.
* This allows you to use any ky option not explicitly exposed in the config.
*/
- kyOptions?: Omit;
+ kyOptions?: Omit;
/**
* Return the response data parsed in a specified format. By default, `auto`
* will infer the appropriate method from the `Content-Type` response header.
diff --git a/packages/openapi-ts/package.json b/packages/openapi-ts/package.json
index 2364676cf3..7336ec6154 100644
--- a/packages/openapi-ts/package.json
+++ b/packages/openapi-ts/package.json
@@ -88,7 +88,7 @@
"@angular/router": "21.1.2",
"axios": "1.13.4",
"eslint": "9.39.2",
- "ky": "1.14.3",
+ "ky": "2.0.0",
"nuxt": "3.14.1592",
"ofetch": "1.5.1",
"rxjs": "7.8.2",
diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-ky/__tests__/client.test.ts b/packages/openapi-ts/src/plugins/@hey-api/client-ky/__tests__/client.test.ts
index aabc518f93..6c9078ff6c 100644
--- a/packages/openapi-ts/src/plugins/@hey-api/client-ky/__tests__/client.test.ts
+++ b/packages/openapi-ts/src/plugins/@hey-api/client-ky/__tests__/client.test.ts
@@ -478,6 +478,35 @@ describe('response interceptor', () => {
describe('error handling', () => {
const client = createClient({ baseUrl: 'https://example.com' });
+ it('uses HTTPError.data when Ky has consumed the response body', async () => {
+ const errorResponse = new Response(JSON.stringify({ message: 'Not found' }), {
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ status: 404,
+ });
+
+ vi.spyOn(errorResponse, 'text').mockRejectedValue(new Error('body already consumed'));
+
+ const httpError = Object.assign(
+ new HTTPError(errorResponse, new Request('https://example.com/test'), {
+ method: 'GET',
+ } as any),
+ { data: { message: 'Not found' } },
+ );
+
+ const mockKy = vi.fn().mockRejectedValue(httpError);
+
+ const result = await client.get({
+ ky: mockKy as Partial as KyInstance,
+ throwOnError: false,
+ url: '/test',
+ });
+
+ expect(result.error).toEqual({ message: 'Not found' });
+ expect(result.response.status).toBe(404);
+ });
+
it('handles HTTP errors with throwOnError: false', async () => {
const errorResponse = new Response(JSON.stringify({ message: 'Not found' }), {
headers: {
diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-ky/bundle/client.ts b/packages/openapi-ts/src/plugins/@hey-api/client-ky/bundle/client.ts
index 299538682e..8bcd873caa 100644
--- a/packages/openapi-ts/src/plugins/@hey-api/client-ky/bundle/client.ts
+++ b/packages/openapi-ts/src/plugins/@hey-api/client-ky/bundle/client.ts
@@ -1,5 +1,5 @@
-import type { HTTPError, Options as KyOptions } from 'ky';
-import ky from 'ky';
+import type { Options as KyOptions } from 'ky';
+import ky, { isHTTPError } from 'ky';
import { createSseClient } from '../../client-core/bundle/serverSentEvents';
import type { HttpMethod } from '../../client-core/bundle/types';
@@ -75,22 +75,44 @@ export const createClient = (config: Config = {}): Client => {
request: Request,
opts: ResolvedRequestOptions,
interceptorsMiddleware: Middleware,
+ kyHttpError?: { bodyConsumed: true; data: unknown },
) => {
const result = {
request,
response,
};
- const textError = await response.text();
- let jsonError: unknown;
+ let error: unknown;
- try {
- jsonError = JSON.parse(textError);
- } catch {
- jsonError = undefined;
+ if (kyHttpError) {
+ if (kyHttpError.data !== undefined) {
+ if (typeof kyHttpError.data === 'string') {
+ let jsonError: unknown;
+ try {
+ jsonError = JSON.parse(kyHttpError.data);
+ } catch {
+ jsonError = undefined;
+ }
+ error = jsonError ?? kyHttpError.data;
+ } else {
+ error = kyHttpError.data;
+ }
+ } else {
+ error = '';
+ }
+ } else {
+ const textError = await response.text();
+ let jsonError: unknown;
+
+ try {
+ jsonError = JSON.parse(textError);
+ } catch {
+ jsonError = undefined;
+ }
+
+ error = jsonError ?? textError;
}
- const error = jsonError ?? textError;
let finalError = error;
for (const fn of interceptorsMiddleware.error.fns) {
@@ -166,9 +188,8 @@ export const createClient = (config: Config = {}): Client => {
try {
response = await kyInstance(request, kyOptions);
} catch (error) {
- if (error && typeof error === 'object' && 'response' in error) {
- const httpError = error as HTTPError;
- response = httpError.response;
+ if (isHTTPError(error)) {
+ response = error.response;
for (const fn of interceptors.response.fns) {
if (fn) {
@@ -176,7 +197,21 @@ export const createClient = (config: Config = {}): Client => {
}
}
- return parseErrorResponse(response, request, opts, interceptors);
+ if (error.data !== undefined) {
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: error.data,
+ });
+ }
+
+ if (!response.bodyUsed) {
+ return parseErrorResponse(response, request, opts, interceptors);
+ }
+
+ return parseErrorResponse(response, request, opts, interceptors, {
+ bodyConsumed: true,
+ data: undefined,
+ });
}
throw error;
diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-ky/bundle/types.ts b/packages/openapi-ts/src/plugins/@hey-api/client-ky/bundle/types.ts
index a6a4a9f898..4a156c1e0a 100644
--- a/packages/openapi-ts/src/plugins/@hey-api/client-ky/bundle/types.ts
+++ b/packages/openapi-ts/src/plugins/@hey-api/client-ky/bundle/types.ts
@@ -34,7 +34,7 @@ export interface RetryOptions {
export interface Config
extends
- Omit,
+ Omit,
CoreConfig {
/**
* Base URL for all requests made by this client.
@@ -49,7 +49,7 @@ export interface Config
* Additional ky-specific options that will be passed directly to ky.
* This allows you to use any ky option not explicitly exposed in the config.
*/
- kyOptions?: Omit;
+ kyOptions?: Omit;
/**
* Return the response data parsed in a specified format. By default, `auto`
* will infer the appropriate method from the `Content-Type` response header.
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7d86236e26..0b6043a5c6 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -563,8 +563,8 @@ importers:
specifier: 3.1.6
version: 3.1.6(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
ky:
- specifier: 1.14.0
- version: 1.14.0
+ specifier: 2.0.0
+ version: 2.0.0
react:
specifier: 19.0.0
version: 19.0.0
@@ -1461,8 +1461,8 @@ importers:
specifier: 9.39.2
version: 9.39.2(jiti@2.6.1)
ky:
- specifier: 1.14.3
- version: 1.14.3
+ specifier: 2.0.0
+ version: 2.0.0
nuxt:
specifier: 3.14.1592
version: 3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@25.2.1)(db0@0.3.4)(encoding@0.1.13)(eslint@9.39.2(jiti@2.6.1))(ioredis@5.9.2)(less@4.4.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-rc.12)(rollup@4.56.0)(sass@1.97.1)(terser@5.44.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.4(typescript@5.9.3))
@@ -1572,8 +1572,8 @@ importers:
specifier: 5.7.4
version: 5.7.4
ky:
- specifier: 1.14.3
- version: 1.14.3
+ specifier: 2.0.0
+ version: 2.0.0
node-fetch:
specifier: 3.3.2
version: 3.3.2
@@ -11722,13 +11722,9 @@ packages:
kuler@2.0.0:
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
- ky@1.14.0:
- resolution: {integrity: sha512-Rczb6FMM6JT0lvrOlP5WUOCB7s9XKxzwgErzhKlKde1bEV90FXplV1o87fpt4PU/asJFiqjYJxAJyzJhcrxOsQ==}
- engines: {node: '>=18'}
-
- ky@1.14.3:
- resolution: {integrity: sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw==}
- engines: {node: '>=18'}
+ ky@2.0.0:
+ resolution: {integrity: sha512-KzI4Vz5AbZFAUFYGx28PCSfFWUo6/qj9Br/P6KRwDieE1xfdz0tIONepJcLw/1xLocN13GgvfJGasa+pfSkbHg==}
+ engines: {node: '>=22'}
lambda-local@2.2.0:
resolution: {integrity: sha512-bPcgpIXbHnVGfI/omZIlgucDqlf4LrsunwoKue5JdZeGybt8L6KyJz2Zu19ffuZwIwLj2NAI2ZyaqNT6/cetcg==}
@@ -27742,9 +27738,7 @@ snapshots:
kuler@2.0.0: {}
- ky@1.14.0: {}
-
- ky@1.14.3: {}
+ ky@2.0.0: {}
lambda-local@2.2.0:
dependencies: