> ## 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 — including a matching request whose vacancy is not in your workspace                                                                     |
| `422` | Unprocessable Entity  | Update payload does not match the expected CV or job update schema, or a matching request references candidates not in your workspace, or a vacancy with no extractable requirements |
| `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_CREDITS",
  "message": "Insufficient credits: 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 /credits/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`.

### Matching against entities not in your workspace

Before running a match, the API verifies that the vacancy and every candidate in the request belong to **your** workspace. If any doesn't, the request is rejected immediately with a coded error — nothing is charged, and the request is not retried. Switch on `code` (not the message).

<Note>
  For isolation, an entity that belongs to a different workspace is reported exactly as if it did not exist. The API never discloses that an ID exists in another workspace.
</Note>

**Vacancy not found** — `404 Not Found`. The vacancy ID does not exist in your workspace (it was never ingested here, was deleted, or belongs to another workspace).

```json theme={null}
{
  "code": "VACANCY_NOT_FOUND",
  "message": "Vacancy 3c90c3cc-0d44-4b50-8888-8dd25736052a was not found in this workspace.",
  "vacancy_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
```

**Fix:** Confirm the vacancy was analyzed in the same workspace whose API key you're using, and that its ID is correct. Ingest the job into this workspace before matching.

**Candidates not found** — `422 Unprocessable Entity`. One or more candidate IDs are not in your workspace. The `candidate_ids` array lists every candidate that could not be used.

```json theme={null}
{
  "code": "CANDIDATES_NOT_FOUND",
  "message": "These candidate(s) were not found in this workspace: 1f15e634-492a-6330-9b0b-6e0e0573a940. Add them to this workspace before matching, or remove them from the request.",
  "candidate_ids": ["1f15e634-492a-6330-9b0b-6e0e0573a940"]
}
```

**Fix:** Ingest those candidates into this workspace first, or drop them from the request. Only candidates analyzed under the same workspace can be matched against its vacancies.

**Vacancy has no requirements** — `422 Unprocessable Entity`. The vacancy exists in your workspace but has no extractable skills or requirements, so there is nothing to score candidates against.

```json theme={null}
{
  "code": "VACANCY_HAS_NO_REQUIREMENTS",
  "message": "Vacancy <id> has no extractable skills or requirements, so candidates can't be scored against it. Improve the vacancy quality — add a clearer job description with explicit required skills, responsibilities, and experience — then re-run matching.",
  "vacancy_id": "<id>"
}
```

**Fix:** Improve the vacancy's quality — provide a clearer job description with explicit required skills, responsibilities, and experience — re-analyze it, then re-run matching.

## 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>
