# Error codes ## Kinds of failure on a recorded call Every failed call is recorded with the provider's own error code, and grouped into one kind, so the same failure reads the same across providers. Group by `error_class` over the API to count them. | `error_class` | Shown as | Whose | What it means | | --- | --- | --- | --- | | `ERROR_CLASS_RATE_LIMITED` | Rate limit exceeded | Provider | Over a rate or quota. Retrying after the wait usually works. | | `ERROR_CLASS_TIMEOUT` | Request timed out | Provider | No answer in time. Retrying may work. | | `ERROR_CLASS_UNAVAILABLE` | Service unavailable | Provider | The provider or tool was down, overloaded or unreachable. | | `ERROR_CLASS_SAFETY_BLOCKED` | Blocked by content filter | Provider | A safety filter stopped the prompt or the answer. | | `ERROR_CLASS_INVALID_REQUEST` | Invalid request | Yours | The provider rejected the request as sent. It fails until the request changes. | | `ERROR_CLASS_NOT_FOUND` | Resource not found | Yours | The model named does not exist, or this account cannot use it. | | `ERROR_CLASS_PERMISSION_DENIED` | Access denied | Yours | The provider credential was missing, wrong or not allowed. | | `ERROR_CLASS_CONTEXT_OVERFLOW` | Context window exceeded | Yours | The request held more tokens than the model accepts. | | `ERROR_CLASS_BILLING` | Billing limit reached | Yours | The provider account cannot pay: a spend limit, an empty balance or billing off. | | `ERROR_CLASS_TOOL_ERROR` | Tool execution failed | Yours | The tool ran and failed in its own code. | | `ERROR_CLASS_CANCELLED` | Request cancelled | Unknown | The caller stopped waiting. Nothing was wrong with the call. | | `ERROR_CLASS_UNKNOWN` | Unknown error | Unknown | A code not yet mapped. The provider's code is shown beside it. | A call that did not fail has the class `ERROR_CLASS_UNSPECIFIED`. Grouped by `error_class`, a row's `errorClassOwner` says whose it is: `ours` for the ones marked Yours above, meaning your own request was at fault, `provider`, or `unknown`. Two statuses are not failures of the call itself: `DENIED`, stopped by your budget before it was sent and never billed, and `TRUNCATED`, stopped at a token limit or deadline after doing part of the work. ## Errors from the API | Status | Why | What to do | | --- | --- | --- | | `400` | A parameter is unknown or badly written: a missing window, a time zone, dimension, filter, read mask or page token that is not valid. | Check the `reason` and `field`, below. | | `401` | The key or secret is missing, unknown or revoked. | Send both headers. Create a new key if this one was revoked. | | `403` | A key without **Read** was used, or the path names another organisation. | Use a read key belonging to the organisation in the path. | | `429` | Over your organisation's rate limit. | Wait the seconds in `Retry-After`, then retry. | Every refusal's body carries a `reason` your code can check, the `field` at fault where one is, and the `requestId` to quote when you contact us: | `reason` | `field` | What to change | | --- | --- | --- | | `UNKNOWN_PARAMETER` | The parameter | Remove it, or check its spelling against the method's page. | | `INVALID_PARAMETER` | The parameter | Send it in the type the method's page gives, once. A time is RFC 3339, such as `2026-10-01T00:00:00Z`, and `orderBy` takes only `occurred_at desc`. | | `MISSING_WINDOW` | `startTime` | Send both `startTime` and `endTime`. | | `INVALID_WINDOW` | `startTime` or `endTime` | Send both, with the end after the start. | | `INVALID_TIME_ZONE` | `timeZone` | Send an IANA name, such as `Africa/Nairobi`. | | `UNKNOWN_DIMENSION` | `groupBy` | Use a dimension the message lists. | | `UNKNOWN_FILTER_FIELD` | `filter` | Filter on a field the message lists. | | `INVALID_FILTER_VALUE` | `filter` | Use a value the message lists for that field. | | `INVALID_FILTER` | `filter` | Write terms of `field = value` joined by `AND`. | | `UNKNOWN_READ_MASK_FIELD` | `readMask` | Name figures the message lists. | | `INVALID_PAGE_TOKEN` | `pageToken` | Pass back the `nextPageToken` exactly as it came. | Any other refusal's `reason` is its `code` in capitals, such as `PERMISSION_DENIED` or `RESOURCE_EXHAUSTED`. ## Errors from the recorders | Error | When | What to do | | --- | --- | --- | | Settings error at startup | An `AP_*` setting is missing or malformed, for example an agent outside the key's organisation. | Fix the setting. The message names it. | | `SpendDenied` | A budget stopped the call. Recognise it with `agentpulse.denied(err)`, `denied(err)` or `recorder.Denied(err)`. | Tell your user in your own words. | | `403` from the LiteLLM proxy | A budget stopped the call. Marked not to be retried. | As above. | Nothing else the recorder does raises into your code. Problems sending records show in its stats instead.