What version of kubb is running?
5.0.0-beta.97
What kind of platform do you use?
Linux
Your kubb.config.ts config file?
Minimal Kubb configuration
import { pluginAxios } from '@kubb/plugin-axios';
import { pluginTs } from '@kubb/plugin-ts';
import { defineConfig } from 'kubb';
export default defineConfig({
input: './openapi.yaml',
output: {
path: './src/api',
clean: true,
},
plugins: [
pluginTs(),
pluginAxios(),
],
});
Swagger/OpenAPI file?
Minimal OpenAPI document
openapi: 3.1.0
info:
title: Reproduction
version: 1.0.0
paths:
/login:
post:
operationId: login
responses:
"200":
description: Successful login
content:
application/json:
schema:
type: object
required: [token]
properties:
token:
type: string
"401":
description: Invalid credentials
content:
application/json:
schema:
type: object
required: [detail]
properties:
detail:
type: string
What version of external packages are you using(@tanstack-query, MSW, React, Vue, ...)
{
"axios": "1.17.0",
"node": "24.16.0",
"pnpm": "10.31.0",
"kubb": "5.0.0-beta.97",
"@kubb/plugin-axios": "5.0.0-beta.98",
"@kubb/plugin-ts": "5.0.0-beta.98"
}
What steps can reproduce the bug?
- Generate the client with the configuration above.
- Configure the generated client to call a server that returns
401 for POST /login.
- Call the generated operation with
throwOnError: true:
import { createClient } from './src/api/.kubb/client';
import { login } from './src/api/clients/login';
const client = createClient({
baseURL: 'http://localhost:3000',
});
try {
const result = await login({
client,
throwOnError: true,
});
console.log('resolved', result);
} catch (error) {
console.log('rejected', error);
}
- Observe that the promise resolves with
status: 401 instead of rejecting with ResponseError.
The Axios client generated by @kubb/plugin-axios does not reject non-2xx responses when throwOnError: true and no custom validateStatus function is provided.
The generated .kubb/client.ts contains:
const throwOnError = requestConfig.throwOnError ?? config.throwOnError ?? true;
const validateStatus =
requestConfig.validateStatus ??
config.validateStatus ??
(throwOnError ? undefined : () => true);
It then includes validateStatus in the Axios request configuration even when its value is explicitly undefined.
Axios treats an explicit validateStatus: undefined as accepting every response status under its default transitional behavior:
https://axios.rest/pages/advanced/request-config#validatestatus
Consequently, a 401, 404, or 500 response resolves instead of rejecting. Kubb's catch block is never reached, so no generated ResponseError is thrown.
This also contradicts the generated TypeScript contract. When throwOnError: true, RequestResult is narrowed to successful response statuses, but at runtime it can resolve with:
{
status: 401,
data: undefined,
error: { /* error body */ }
}
For React Query consumers, this can cause a failed HTTP request to resolve its query or mutation instead of entering the error state and invoking onError.
This happens every time a non-2xx response is returned with:
throwOnError: true
- No request-level
validateStatus
- No client-level
validateStatus
Suggested fix
Use an explicit 2xx validator when throwOnError is enabled:
const validateStatus =
requestConfig.validateStatus ??
config.validateStatus ??
(throwOnError
? (status: number) => status >= 200 && status < 300
: () => true);
This preserves custom request/client validators while making the default behavior agree with throwOnError, ResponseError, and the generated RequestResult type.
How often does this bug happen?
Every time
What is the expected behavior?
With throwOnError: true, only successful 2xx statuses should be accepted. Non-2xx responses should reject and be converted into Kubb's generated ResponseError.
With throwOnError: false, all statuses should continue resolving as discriminated { status, data, error } results.
Additional information
Axios documents that its default validator rejects statuses outside 200–299, but an explicitly supplied validateStatus: undefined resolves all statuses under the default transitional configuration:
https://axios.rest/pages/advanced/request-config#validatestatus
What version of
kubbis running?5.0.0-beta.97
What kind of platform do you use?
Linux
Your
kubb.config.tsconfig file?Swagger/OpenAPI file?
Minimal OpenAPI document openapi: 3.1.0 info: title: Reproduction version: 1.0.0 paths: /login: post: operationId: login responses: "200": description: Successful login content: application/json: schema: type: object required: [token] properties: token: type: string "401": description: Invalid credentials content: application/json: schema: type: object required: [detail] properties: detail: type: stringWhat version of external packages are you using(
@tanstack-query,MSW,React,Vue, ...){ "axios": "1.17.0", "node": "24.16.0", "pnpm": "10.31.0", "kubb": "5.0.0-beta.97", "@kubb/plugin-axios": "5.0.0-beta.98", "@kubb/plugin-ts": "5.0.0-beta.98" }What steps can reproduce the bug?
401forPOST /login.throwOnError: true:status: 401instead of rejecting withResponseError.The Axios client generated by
@kubb/plugin-axiosdoes not reject non-2xx responses whenthrowOnError: trueand no customvalidateStatusfunction is provided.The generated
.kubb/client.tscontains:It then includes
validateStatusin the Axios request configuration even when its value is explicitlyundefined.Axios treats an explicit
validateStatus: undefinedas accepting every response status under its default transitional behavior:https://axios.rest/pages/advanced/request-config#validatestatus
Consequently, a
401,404, or500response resolves instead of rejecting. Kubb'scatchblock is never reached, so no generatedResponseErroris thrown.This also contradicts the generated TypeScript contract. When
throwOnError: true,RequestResultis narrowed to successful response statuses, but at runtime it can resolve with:For React Query consumers, this can cause a failed HTTP request to resolve its query or mutation instead of entering the error state and invoking
onError.This happens every time a non-2xx response is returned with:
throwOnError: truevalidateStatusvalidateStatusSuggested fix
Use an explicit 2xx validator when
throwOnErroris enabled:This preserves custom request/client validators while making the default behavior agree with
throwOnError,ResponseError, and the generatedRequestResulttype.How often does this bug happen?
Every time
What is the expected behavior?
With
throwOnError: true, only successful 2xx statuses should be accepted. Non-2xx responses should reject and be converted into Kubb's generatedResponseError.With
throwOnError: false, all statuses should continue resolving as discriminated{ status, data, error }results.Additional information
Axios documents that its default validator rejects statuses outside
200–299, but an explicitly suppliedvalidateStatus: undefinedresolves all statuses under the default transitional configuration:https://axios.rest/pages/advanced/request-config#validatestatus