Skip to main content

Error categories

When a request fails, kweri categorizes the error before caching it. The category determines whether the error is retryable:
TypeConditionRetryable
validationValidationError or HTTP 4xx responseNo
serverHTTP 5xx responseYes
networkTypeError, AbortError, network unreachableYes
unknownAnything elseYes
interface CachedError {
  message: string
  type: 'network' | 'validation' | 'server' | 'unknown'
  status?: number   // HTTP status code, if available
  retryable: boolean
}

Automatic retry

Retries only fire for errors marked retryable: true. The delay uses exponential backoff with jitter:
delay = min(1000ms × 2^attempt, 30_000ms) + random(0, 1000ms)
AttemptBase delayWith jitter
12s~2–3s
24s~4–5s
38s~8–9s
416s~16–17s
5+30s (cap)~30–31s
Configure the maximum number of retries on the Kweri instance:
const kweri = new Kweri({
  baseURL: 'https://api.example.com',
  maxRetries: 3  // default: 0 (no retries)
})
Setting maxRetries to 0 disables retries entirely. The default is intentionally conservative — enable retries explicitly for your use case.

Error caching

Errors are cached for a short window (default 5 seconds). During this window, subsequent calls to kweri.query() for the same key return the cached error immediately rather than hammering the server. After the error cache expires, the next query call fires a fresh request.

Accessing errors in hooks

const { data, error, isError, status } = useQuery(kweri, getUsers, {})

if (isError) {
  return <p>Failed: {error?.message}</p>
}

ValidationError

ValidationError is thrown when the server response doesn’t match the endpoint’s response schema — i.e. a contract mismatch between your definition and what the server actually returned:
import { ValidationError } from 'kweri'

try {
  await kweri.query(getUsers, {})
} catch (err) {
  if (err instanceof ValidationError) {
    console.log(err.errors)
    // [{ path: '/0/email', message: 'Expected string' }]
  }
}
ValidationError is not retryable.
Params are not validated at runtime. The endpoint params schema drives TypeScript type inference only. Passing wrong params is a compile-time error, not a runtime one.

Custom error handling with a custom fetcher

If your API returns errors in a non-standard shape (e.g., { error: '...' } with a 200 status), use a custom fetcher to normalize them:
const kweri = new Kweri({
  baseURL: 'https://api.example.com',
  fetcher: async ({ method, url, body }) => {
    const res = await fetch(url, {
      method,
      headers: { 'Content-Type': 'application/json' },
      body: body ? JSON.stringify(body) : undefined
    })

    if (!res.ok) {
      throw new Error(`HTTP ${res.status}: ${res.statusText}`)
    }

    const json = await res.json()

    // Unwrap API-level errors
    if (json.error) {
      throw new Error(json.error)
    }

    return res  // kweri expects a Response-like object
  }
})