> ## Documentation Index
> Fetch the complete documentation index at: https://docs.freshtalent.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Application and framework error shapes, status codes, and safe retry decisions.

## Error envelope

Application errors usually use this shape. Framework validation, rate limiting, and unhandled failures can differ; branch on HTTP status first.

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "error": {
    "type": "invalid_request",
    "message": "since must be ISO-8601 or a relative window like 24h"
  }
}
```

## Error types

| Status | `error.type`                  | Meaning                                                 |
| ------ | ----------------------------- | ------------------------------------------------------- |
| `400`  | `invalid_request`             | Malformed body or invalid parameter value               |
| `401`  | `unauthorized`                | Missing, malformed, or revoked key                      |
| `404`  | `not_found`                   | Resource does not exist                                 |
| `409`  | `conflict`                    | Conflicting resource state; see the endpoint contract   |
| `402`  | `payment_required`            | Subscription required by the organization access gate   |
| `403`  | `forbidden`                   | Operation not allowed, such as modifying a default list |
| `429`  | *(framework response)*        | Gateway rate limit exceeded; honor `Retry-After`        |
| `5xx`  | *(may be framework response)* | Server failure; retry reads, reconcile writes           |

<Note>
  FreshTalent does not use a credits envelope. Application errors are typed by `error.type`; framework errors may differ — see [Authentication](/authentication) and [Rate limits](/rate-limits) for the common cases.
</Note>

## Examples

```json 400 invalid_request theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "error": {
    "type": "invalid_request",
    "message": "min_role_months requires role_family"
  }
}
```

```json 404 not_found theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "error": {
    "type": "not_found",
    "message": "no person 'unknown-slug'"
  }
}
```

```json 402 payment_required theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "error": {
    "type": "payment_required",
    "message": "Trial ended. Subscribe to keep using FreshTalent."
  }
}
```

The exact statuses a route returns are listed on its reference page. Some legacy error messages use `watch` for a Target; treat the status and typed error as the contract, not the display wording.

## Retries

* Retry GET requests on transient network failures, `429`, and `5xx` with bounded exponential backoff, jitter, and a timeout. Honor `Retry-After` when supplied.
* Do not automatically retry `400`, `401`, `402`, `403`, `404`, or `409` without addressing the cause.
* Do not blindly replay POST/PATCH after a timeout: the write may already have succeeded. Read the resource first. Adding list members can overwrite existing stage and notes.
* Deduplicate downstream event writes by event UUID; a crash between the external write and your checkpoint can cause a replay. Use an idempotent destination or transactional outbox.

## Framework error shape

Validation and rate-limit failures can instead use this shape (message is illustrative):

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "statusCode": 429,
  "error": "Too Many Requests",
  "message": "Rate limit exceeded, retry in 1 minute"
}
```

Do not assume `error` is always an object, or parse human-readable messages as stable identifiers. See the [runnable client](/guides/first-integration).
