# Errors

> Every HTTP status and error message the FluentC API returns, what causes it, and which ones are safe to retry.

Source: https://www.fluentc.ai/docs/api/errors/

## Error shape

Errors are JSON with a single `error` string, and the HTTP status tells you the class of problem.

```json
{ "error": "Target language 'es' is not enabled for this site." }
```

Branch on the status code first. Match on the message only for the specific cases below; the messages are quoted exactly as the API returns them.

The one exception: a batch job that couldn't be translated is not an HTTP error. [Batch results](https://www.fluentc.ai/docs/api/batch-results.md) returns `200` with `"status": "failed"` and an `error` string.

## Status codes

| Status | Meaning | Retry? |
| --- | --- | --- |
| `200` | Success. For batch results, check `status`. | n/a |
| `401` | Authentication failed. | No. Fix the key. |
| `403` | The key is valid but the account can't make this request. | No. Fix the account. |
| `422` | The request is invalid. | No. Fix the request. |
| `500` | Something went wrong on our side. | Yes, with backoff. |

## 401 Unauthorized

| Message | Cause |
| --- | --- |
| `API key (Site UUID) is missing` | The `Authorization` header is absent or empty. |
| `Invalid API key: Site not found.` | The key is wrong, has extra characters, or has a `Bearer` prefix. |
| `Invalid API key: Associated company not found.` | The key's account can't be resolved. [Contact us](https://www.fluentc.ai/contact/). |

## 403 Forbidden

| Message | Cause |
| --- | --- |
| `Access denied: Translations are disabled for this site.` | Translation is switched off for the key's site. |
| `Subscription error: ...` | The subscription isn't active, or has no languages. The text after the colon says which. |
| `Usage limit error: ...` | A usage limit was reached. The text after the colon says which. |

## 422 Unprocessable Entity

| Message | Endpoint | Cause |
| --- | --- | --- |
| `Input is missing` | translate, checklanguage | `input` is empty or absent. |
| `Invalid or missing input format. Allowed formats: text, html, json` | translate | `input_format` isn't one of the three. |
| `Invalid or missing input format. Allowed formats: text, html` | checklanguage | `input_format` isn't `text` or `html`. |
| `Invalid or missing target language code.` | translate | `target_language` isn't two or three lowercase letters. |
| `Target language 'xx' is not enabled for this site.` | translate | Valid code, but not enabled for your key. |
| `Invalid source language code: 'xx'.` | translate | `source_language` isn't two or three letters. |
| `Source language 'xx' is not enabled for this site.` | translate | Valid code, but not enabled for your key. |
| `Invalid or missing mode. Allowed modes: batch, real-time` | translate | `mode` is absent or unknown. |
| `Realtime translation is not supported for this agent.` | translate | `real-time` on a Batch plan key. |
| `Agent translation is not supported for this site.` | translate | The key isn't set up for the translation API. |
| `Invalid JSON format` | translate | `input_format` is `json` but `input` isn't valid JSON. |
| `job_id (hash_key) is missing` | results | The body has no `job_id`. |

## 500 Internal Server Error

```json
{ "error": "An unexpected error occurred during authentication." }
```

Retry with exponential backoff. If it keeps happening, [contact us](https://www.fluentc.ai/contact/) with the time of the request and the `job_id` if there was one.

## Retrying

- **Do retry** `500` responses and network timeouts. Wait 1 second, then 2, 4, 8, up to a limit you choose, and add a little random jitter.
- **Back off** if you hit a plan limit. Rate limits depend on your plan; the Batch plan allows 20 calls per hour. Treat `429` the same way as `500`.
- **Don't retry** `401`, `403` or `422` unchanged. They will fail the same way.
- **Batch requests are safe to resubmit.** The `job_id` is deterministic for the same input and target language.

```js
async function translateWithRetry(body, attempts = 4) {
  for (let i = 0; i < attempts; i++) {
    const res = await fetch("https://api.fluentc.ai/ai_agent/translate", {
      method: "POST",
      headers: {
        Authorization: process.env.FLUENTC_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(body),
    });
    if (res.ok) return res.json();

    const retryable = res.status === 429 || res.status >= 500;
    if (!retryable || i === attempts - 1) {
      const { error } = await res.json().catch(() => ({}));
      throw new Error(`${res.status}: ${error ?? res.statusText}`);
    }
    const delay = 1000 * 2 ** i + Math.random() * 250;
    await new Promise((resolve) => setTimeout(resolve, delay));
  }
}
```
