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

# Error Handling

> API error responses, HTTP status codes, and processing statuses

# Error Handling

The Fribl API uses standard HTTP status codes and returns JSON error bodies to indicate what went wrong.

## Error response format

Most API errors follow this structure:

```json theme={null}
{
  "error": "Human-readable error description"
}
```

Update endpoints can also proxy structured validation errors from the ingestion service:

```json theme={null}
{
  "detail": {
    "message": "Update validation failed.",
    "invalid_skills": [
      {
        "id": "not-a-real-skill-id",
        "field": "hard_skills",
        "message": "Skill does not exist in the canonical graph/taxonomy."
      }
    ]
  }
}
```

Schema mismatches on update use `422 Unprocessable Entity` with structured details:

```json theme={null}
{
  "detail": {
    "message": "Payload does not match VacancyUpdate schema for entity_type=Vacancy.",
    "schema": "VacancyUpdate",
    "errors": [
      {
        "type": "missing",
        "loc": ["information", "title"],
        "msg": "Field required"
      }
    ]
  }
}
```

## HTTP status codes

| Code  | Meaning               | When it occurs                                                                                                          |
| ----- | --------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `200` | OK                    | Request succeeded                                                                                                       |
| `400` | Bad Request           | Invalid request body, missing required fields, invalid file format, weight sum exceeds 1.0, or update validation failed |
| `401` | Unauthorized          | Missing or invalid `x-api-key` header                                                                                   |
| `402` | Payment Required      | Workspace has insufficient tokens for the requested operation                                                           |
| `404` | Not Found             | Requested CV, job, or match does not exist                                                                              |
| `422` | Unprocessable Entity  | Update payload does not match the expected CV or job update schema                                                      |
| `429` | Too Many Requests     | Rate limit exceeded                                                                                                     |
| `500` | Internal Server Error | Unexpected server-side failure                                                                                          |
| `502` | Bad Gateway           | Upstream service error (sourcing endpoints)                                                                             |
| `503` | Service Unavailable   | Required external service is not configured (sourcing endpoints)                                                        |

## Common error examples

### Invalid API key

```json theme={null}
{
  "error": "Invalid API Key."
}
```

**Fix:** Verify your `x-api-key` header contains a valid key.

### Insufficient tokens

Billable endpoints return `402 Payment Required` with a structured body when your workspace can't cover the request. No work is performed and nothing is charged.

```json theme={null}
{
  "code": "INSUFFICIENT_TOKENS",
  "message": "Insufficient tokens: have 3, need 10",
  "balance": 3,
  "required": 10
}
```

**Fix:** Switch on `code` (not the message), add tokens in the [Fribl Console](https://console.fribl.co), confirm with `GET /tokens/balance`, then retry — charging is idempotent, so re-sending the same request is safe. See [Tokens & Billing](/tokens) for the full pricing and refund model.

### Missing required field

```json theme={null}
{
  "error": "Missing required fields."
}
```

**Fix:** Ensure your request body includes all required fields. For analyze endpoints, the `inputs` field must be a non-empty array of strings.

### Update validation failure

```json theme={null}
{
  "detail": {
    "message": "content_language is required and must be a valid ISO 639-1 language code."
  }
}
```

**Fix:** On `PUT /jobs/{id}` and `PUT /cvs/{id}`, send `content_language` (or set the `Accept-Language` header — the API auto-forwards it), include at least one allowed dimension (`information`, `experience`/`experience_preference`, `education`/`education_preference`, `hard_skills`, `soft_skills`), and use split `hard_skills` / `soft_skills` arrays with canonical lowercase relationship values: `required`, `optional`, `satisfies`.

### Resource not found

```json theme={null}
{
  "error": "CV not found"
}
```

**Fix:** Verify the ID exists and that the resource has finished processing. Check with the status endpoint first.

### Match weight validation

```json theme={null}
{
  "error": "Sum of weights cannot be greater than 1"
}
```

**Fix:** Ensure that `experience + hardSkills + softSkills + education` in your weights object does not exceed `1.0`.

## Processing statuses

CV and job analysis is asynchronous. After submitting a document, poll the status endpoint to track progress. The public API uses a single normalized vocabulary across `POST /analyze`, `POST /analyze/files`, `POST /{cvs,jobs}/status`, and `GET /{cvs,jobs}/{id}`:

| Status      | Meaning                                                                                                           |
| ----------- | ----------------------------------------------------------------------------------------------------------------- |
| `PENDING`   | The task is queued, actively being processed, or being retried after a transient failure. Keep polling.           |
| `COMPLETED` | Analysis is finished — retrieve results or run matching.                                                          |
| `FAILED`    | Analysis errored out — resubmit the document.                                                                     |
| `NOT_FOUND` | No record exists for that ID in the requested resource type. Returned only by the batch `POST /status` endpoints. |

<Note>
  **Failed tasks are refunded.** When a task ends in `FAILED`, the tokens charged for it are returned automatically — you only pay for work that completes. The same applies to server errors (`5xx`). See [Tokens & Billing](/tokens#refunds-and-idempotency).
</Note>

<Tip>
  Poll the status endpoint at reasonable intervals (e.g., every 2–3 seconds) rather than in a tight loop. Continue polling while the status is `PENDING`. Typical analysis completes within a few seconds.
</Tip>

<Note>
  `GET /cvs/{id}` and `GET /jobs/{id}` use HTTP status codes to signal readiness: an in-flight task returns **404 with body `{ "message": "...", "status": "PENDING" }`** (keep polling), and a ready entity returns **200 with `status: "COMPLETED"`**. A 404 with an `{ "error": "..." }` body means the ID does not exist.
</Note>
