Profiles·Public

ofetch

semver>=1.0.0postconditions6functions3last verified2026-06-24coverage score100%

Postconditions: what we check

  • ofetch · ofetch-no-try-catch
    error
    Whenawaited ofetch() call is not wrapped in try/catch
    ThrowsFetchError
    Required handlingCaller MUST wrap ofetch() in try/catch. Unlike native fetch(), ofetch automatically throws FetchError when response.ok is false (any non-2xx status). FetchError has .status, .statusCode, .data (parsed response body), .message, .request, and .response properties. Without try/catch, any 4xx or 5xx response causes an unhandled exception.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1][2]
  • ofetch · ofetch-timeout-unhandled
    error
    Whenofetch() called with the timeout option set (e.g., { timeout: 3000 }) and the request does not complete within the timeout window.
    ThrowsFetchError wrapping a TimeoutError as its cause. The FetchError message includes "[TimeoutError]: The operation was aborted due to timeout". The cause error has name "TimeoutError" and code 23 (DOMException TIMEOUT_ERR). This is distinct from network-error FetchErrors which have no .cause.
    Required handlingCaller MUST wrap ofetch() in try/catch when using the timeout option: try { const data = await ofetch('/api/data', { timeout: 5000 }); } catch (error) { if (error.cause?.name === 'TimeoutError') { // Request timed out — retry, return cached, or surface error to user } throw error; } Without try/catch, a timeout causes an unhandled FetchError that crashes the calling async context. Timeout errors are NOT retried (retries are suppressed for timeout-triggered aborts per source: isAbort check excludes errors where context.options.timeout is set).
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3][4]
  • ofetch · ofetch-retry-post-disabled
    warning
    Whenofetch() called with a POST, PUT, PATCH, or DELETE method (explicitly set or inferred from body presence) and the server returns a retryable status code (500, 502, 503, 504, 429, etc.) but the caller expects automatic retry.
    ThrowsFetchError immediately after the first failure — no automatic retry occurs. The default retry count for POST/PUT/PATCH is 0 (disabled) to avoid unintended side effects from repeated writes.
    Required handlingFor idempotent POST/PUT operations that should retry, explicitly set retry: await ofetch('/api/resource', { method: 'POST', body: { ... }, retry: 3, // explicit retry count overrides the default retryDelay: 500, // ms between retries }); For non-idempotent writes, do NOT enable retry — repeated execution can cause duplicate records, double-charges, or duplicate emails. Wrap in try/catch and implement application-level retry with idempotency keys instead.
    costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[5][3]
  • $fetch · dollar-fetch-no-try-catch
    error
    Whenawaited $fetch() call is not wrapped in try/catch
    ThrowsFetchError
    Required handlingCaller MUST wrap $fetch() in try/catch. $fetch is an alias for ofetch and has identical behavior: automatically throws FetchError when response.ok is false. FetchError has .status, .statusCode, .data, .message, .request, .response properties.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1][2]
  • raw · raw-still-throws-on-error
    error
    Whenofetch.raw() is called without try/catch and the server returns a 4xx or 5xx status code. Callers who switch from native fetch() to ofetch.raw() expecting the native fetch() behavior (no throw on non-2xx) will be surprised.
    ThrowsFetchError — identical to ofetch(). The raw suffix refers to the return type (full FetchResponse object vs. just ._data), NOT to bypassing error handling. FetchError.response contains the full response; FetchError.data contains the parsed error body; FetchError.status contains the HTTP status code.
    Required handlingCaller MUST wrap ofetch.raw() in try/catch: try { const response = await ofetch.raw('/api/data'); console.log(response.status, response.headers.get('x-request-id')); const data = response._data; } catch (error) { // error is FetchError — use error.status, error.data, error.response if (error.status === 404) { ... } } To bypass error throwing (and inspect all responses including errors), use ignoreResponseError: true option — this makes ofetch.raw() behave like native fetch() and return the response for all status codes.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[6][3]
  • raw · raw-timeout-unhandled
    error
    Whenofetch.raw() called with timeout option and the request does not complete within the specified timeout window.
    ThrowsFetchError wrapping TimeoutError as cause. Identical timeout behavior to ofetch() — same $fetchRaw internal function, same error path.
    Required handlingSame as ofetch() with timeout: wrap in try/catch and check error.cause?.name === 'TimeoutError' to distinguish timeout from HTTP errors.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3][4]

Sources

Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.

Source code

Research notes

Curator notes from SOURCES.md captured when the profile was written so you can verify the reasoning, not just the rules.

Sources — ofetch

Documentation Sources

URLFetchedSummary
https://raw.githubusercontent.com/unjs/ofetch/main/README.md2026-04-02Main README — FetchError thrown on non-2xx responses, .data/.status/.statusCode properties
https://raw.githubusercontent.com/unjs/ofetch/main/src/error.ts2026-04-02FetchError class definition — extends Error, has request/options/response/data/status/statusCode properties
https://github.com/unjs/ofetch2026-04-02Repository homepage

Key Error Type

FetchError (extends Error):

  • .status / .statusCode — HTTP status code from response
  • .data — Parsed response body
  • .message — Format: "[METHOD] URL: STATUS_CODE STATUS_TEXT"
  • .request — The original request
  • .response — The full response object

Error Trigger

Any response where response.ok === false (non-2xx) automatically throws FetchError. Can be suppressed with ignoreResponseError: true option.

Real-World Usage

  • ourongxing/newsnow: $fetch.create() pattern, then calls like myFetch('/me/sync', { method: 'POST' }) in hooks without try/catch
Need a different package?
Request a profile