Skip to main content
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

message
string
Formatted error message in the format: [METHOD] "url": status statusText errorMessageExample: [GET] "/api/user": 404 Not Found
status
number | undefined
HTTP status code of the response (e.g., 404, 500)
statusCode
number | undefined
Alias for status
statusText
string | undefined
HTTP status text (e.g., “Not Found”, “Internal Server Error”)
statusMessage
string | undefined
Alias for statusText
data
any
The parsed response body, same as response._data. This is particularly useful for API errors that return error details in the response body.
request
FetchRequest
The request URL string or Request object that was used
response
FetchResponse | undefined
The Response object with the parsed data available as _data property
options
FetchOptions
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:
ignoreResponseError
boolean
default:"false"
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)
  }
}

Build docs developers (and LLMs) love