ofetch automatically throws errors for failed HTTP requests (status codes 400-599), making error handling straightforward and consistent.
Automatic Error Throwing
Unlike native fetch, ofetch automatically throws errors when the response status is 4xx or 5xx:
import { ofetch } from 'ofetch'
try {
const data = await ofetch('/api/user/999')
} catch (error) {
console.error(error)
// FetchError: [GET] "/api/user/999": 404 Not Found
}
This eliminates the need to manually check response.ok after every request.
FetchError Type
When a request fails, ofetch throws a FetchError with detailed information:
import { ofetch, FetchError } from 'ofetch'
try {
await ofetch('/api/protected', {
method: 'POST',
body: { name: 'test' }
})
} catch (error) {
if (error instanceof FetchError) {
console.log(error.message) // [POST] "/api/protected": 403 Forbidden
console.log(error.status) // 403
console.log(error.statusCode) // 403 (alias)
console.log(error.statusText) // "Forbidden"
console.log(error.statusMessage)// "Forbidden" (alias)
console.log(error.request) // Request object or URL string
console.log(error.response) // Response object
console.log(error.data) // Parsed response body
console.log(error.options) // Request options used
}
}
FetchError Properties
Formatted error message in the format: [METHOD] "url": status statusText errorMessageExample: [GET] "/api/user": 404 Not Found
HTTP status code of the response (e.g., 404, 500)
HTTP status text (e.g., “Not Found”, “Internal Server Error”)
The parsed response body, same as response._data. This is particularly useful for API errors that return error details in the response body.
The request URL string or Request object that was used
response
FetchResponse | undefined
The Response object with the parsed data available as _data property
The resolved request options that were used
Accessing Error Data
The error.data property contains the parsed response body, which is useful for API errors:
import { ofetch } from 'ofetch'
try {
await ofetch('/api/users', {
method: 'POST',
body: { email: 'invalid-email' }
})
} catch (error) {
// Access structured error information from the API
console.log(error.data)
// {
// status: 400,
// message: "Validation failed",
// errors: {
// email: "Invalid email format"
// }
// }
// Display user-friendly error message
if (error.data?.errors) {
for (const [field, message] of Object.entries(error.data.errors)) {
console.error(`${field}: ${message}`)
}
}
}
The error.data property is the same as error.response._data, providing convenient access to the parsed response body.
Ignoring Response Errors
You can disable automatic error throwing using the ignoreResponseError option:
When set to true, ofetch will not throw errors for 4xx and 5xx responses. Instead, it returns the response normally.
import { ofetch } from 'ofetch'
// Returns response object instead of throwing
const response = await ofetch('/api/resource', {
ignoreResponseError: true
})
if (response.status === 404) {
console.log('Resource not found')
} else if (response.status >= 500) {
console.log('Server error')
}
This is useful when you need to handle different status codes explicitly:
const response = await ofetch('/api/check', {
ignoreResponseError: true
})
switch (response.status) {
case 200:
console.log('Success:', response._data)
break
case 404:
console.log('Not found')
break
case 403:
console.log('Forbidden')
break
default:
console.log('Other status:', response.status)
}
Error Status Codes
ofetch throws errors for all status codes between 400 and 599 (inclusive):
- 4xx Client Errors: 400-499 (Bad Request, Unauthorized, Forbidden, Not Found, etc.)
- 5xx Server Errors: 500-599 (Internal Server Error, Bad Gateway, Service Unavailable, etc.)
Error Cause
When the underlying fetch operation fails (network error, timeout, etc.), the original error is preserved in the cause property:
try {
await ofetch('https://invalid-domain.example.com')
} catch (error) {
console.log(error.cause) // Original network error
}
ofetch polyfills the cause property for runtimes that don’t support it natively.
TypeScript Error Handling
You can type the error data for better TypeScript support:
import { ofetch, FetchError } from 'ofetch'
interface ApiError {
message: string
code: string
errors?: Record<string, string>
}
try {
await ofetch('/api/users', { method: 'POST', body: {} })
} catch (error) {
if (error instanceof FetchError<ApiError>) {
// error.data is now typed as ApiError
console.log(error.data?.message)
console.log(error.data?.code)
}
}