# Fehler und Ratenlimits

> Alle Fehler der OpenCheese-API: JSON-Formate je Endpunkt, Status- und Fehlercodes, Ratenlimit-Header und bei welchen Fehlern sich ein neuer Versuch lohnt.
>
> Webseite: https://opencheese.dev/de/docs/errors/ · Alle Seiten der Doku: https://opencheese.dev/de/docs.md

Fehler kommen als HTTP-Status mit einem JSON-Body im Format des Endpunkts, den du aufgerufen hast.
Jede Meldung beginnt mit 🧀, damit du eine Meldung von OpenCheese vom Text des Modells unterscheiden
kannst, und jede Antwort enthält den Header `x-request-id`: Gib ihn an, wenn du dich an den Support
wendest.

## Fehlerformate

OpenAI-Endpunkte (`/v1/chat/completions`, `/v1/responses`, `/v1/images/*`, `/v1/models` und die
Konto-Endpunkte):

```json
{
  "error": {
    "message": "🧀 Invalid or disabled OpenCheese API key.",
    "type": "authentication_error",
    "code": "invalid_api_key"
  }
}
```

Der Anthropic-Endpunkt (`/v1/messages`) verpackt dieselben Felder im Anthropic-Format:

```json
{
  "type": "error",
  "error": {
    "message": "🧀 Invalid or disabled OpenCheese API key.",
    "type": "authentication_error",
    "code": "invalid_api_key"
  }
}
```

Bei Fehlern, bei denen sich Warten lohnt, enthält der Body zusätzlich `retry_after` (in Sekunden)
und die Antwort einen `Retry-After`-Header.

## Fehler nach dem Start eines Streams

Hat eine Streaming-Antwort einmal begonnen, ist der HTTP-Status bereits `200`; ein Fehler kommt
deshalb im Stream:

- `/v1/chat/completions`: Die Meldung kommt als Text des Assistenten, danach ein letzter Chunk und
  `data: [DONE]`.
- `/v1/responses`: ein `response.failed`-Event mit `response.error.code` und
  `response.error.message`.
- `/v1/messages`: ein `error`-Event mit dem Fehlerobjekt von Anthropic.

## Chat Completions meldet manche Fehler als Antwort des Assistenten

Auf `/v1/chat/completions` kommen Probleme, um die du dich kümmern musst, als Antwort des
Assistenten: in einer `200`-Antwort, gestreamt als `text/event-stream`, auch wenn `stream` auf
`false` steht:

```text
data: {"object":"chat.completion.chunk","model":"opencheese","choices":[{"index":0,"delta":{"role":"assistant","content":"🧀 Invalid or disabled OpenCheese API key."},"finish_reason":"stop"}]}

data: [DONE]
```

Das betrifft einen ungültigen oder deaktivierten Schlüssel, ein gesperrtes Konto, einen ungültigen
Sitzungs-Header, einen Body, der keine gültige Anfrage ist, eine Anfrage, die die Inhaltsrichtlinie
blockiert, und jedes Guthaben- oder Tariflimit. So zeigen dir Chat-Apps die Meldung an. Prüfe in
deinem eigenen Code den `Content-Type`: `text/event-stream` bei einer Anfrage ohne Streaming
bedeutet, dass die Antwort eine solche Meldung ist. Alle anderen Fehler dieses Endpunkts kommen mit
einem normalen Status und einem JSON-Body.

## Codes

| Code | Status | Was passiert ist | Wiederholen? |
| --- | --- | --- | --- |
| `invalid_api_key` | 401 | Der Schlüssel ist falsch, widerrufen oder deaktiviert. | Nein |
| `account_banned`, `account_frozen`, `account_disabled` | 403 | Das Konto kann keine Anfragen stellen; das Dashboard nennt den Grund. | Nein |
| `invalid_request_error` | 400 | Der Body ist keine gültige Anfrage für diesen Endpunkt. | Nein |
| `invalid_session_header` | 400 | `opencheese-session-uuid` ist keine gültige ID; siehe [Sitzungen](https://opencheese.dev/de/docs/sessions.md). | Nein |
| `model_not_found` | 404 | Kein Modell hat diese ID. Sieh in [`/v1/models`](https://opencheese.dev/de/docs/models.md) nach. | Nein |
| `model_access_denied` | 403 | Das Modell ist auf deiner Seite **Preise** ausgeschaltet, oder die Modellliste des Schlüssels enthält es nicht. | Nein |
| `model_disabled`, `model_pricing_disabled` | 400 | Das Modell wird derzeit nicht angeboten. | Nein |
| `unsupported_model` | 400 | Falscher Endpunkt für das Modell, etwa ein Textmodell auf `/v1/images/generations`. | Nein |
| `streaming_unsupported` | 400 | Das Modell unterstützt kein Streaming; sende die Anfrage ohne `stream`. | Nein |
| `unsupported_reasoning_effort` | 400 | Das Modell hat keine solche Reasoning-Stufe; `supported_reasoning_levels` listet seine Stufen auf. | Nein |
| `context_length_exceeded` | 400 | Der Prompt ist länger als das Kontextfenster des Modells. | Nein |
| `request_blocked_by_guard` | 403 | Die Inhaltsrichtlinie hat die Anfrage blockiert. | Nein |
| `provider_refusal` | 400 | Das Modell hat die Antwort verweigert. | Nur mit geänderter Anfrage |
| `moderation_blocked`, `moderation_blocked_billed` | 400 | Die Inhaltsrichtlinie des Modells hat den Prompt oder das Bild abgelehnt. | Nur mit geänderter Anfrage |
| `plan_limit_reached` | 429 | Das Guthaben, ein Zeitfenster des Tarifs, das Ausgabenlimit eines Schlüssels oder ein Monatslimit ist aufgebraucht, oder das Modell gehört nicht zu deinem Tarif. | Nach dem Zurücksetzen oder nach dem Aufladen |
| `rate_limit_exceeded` | 429 | Zu viele Anfragen; siehe unten. | Nach `Retry-After` |
| `requested_effort_unavailable` | 429 | Die Reasoning-Stufe ist vorübergehend nicht verfügbar; der Fehler nennt die anderen Stufen. | Nach `Retry-After` oder mit einer anderen Stufe |
| `provider_unavailable` | 429 | Das Modell ist vorübergehend nicht verfügbar. | Nach `Retry-After` |
| `provider_overloaded` | 5xx | Das Modell ist überlastet. | Ja, mit Backoff, oder mit einem anderen Modell |
| `request_timeout` | 504 | Die Antwort hat zu lange gedauert. | Ja |
| `service_unavailable` | 503 | Das Gateway ist kurz ausgelastet. | Ja, nach kurzer Zeit |

Auf `/v1/chat/completions` kommen die Fehler, die als Antwort des Assistenten eintreffen (siehe
oben), mit Status `200` statt mit dem hier genannten.

Auf `/v1/messages` sendet `plan_limit_reached` zusätzlich `x-should-retry: false`, damit die
Anthropic-SDKs die Anfrage nicht erneut senden.

## Eine Reasoning-Stufe, die das Modell nicht anbietet

```json
{
  "error": {
    "message": "🧀 Example Model is not available for Ultra effort. Choose one of these efforts: low, medium, high.",
    "type": "invalid_request_error",
    "code": "unsupported_reasoning_effort",
    "supported_reasoning_levels": ["low", "medium", "high"]
  }
}
```

Die Stufen reichen von der schwächsten bis zur stärksten und sind dieselben, die `/v1/models` für
das Modell auflistet. Ein Modell ohne Stufen antwortet, dass es keine Reasoning-Einstellung annimmt:
Lass das Feld dann weg. Sendest du gar keine Stufe, gilt die Standardstufe des Modells, und dieser
Fehler tritt nie auf.

## Ratenlimits

Jedes Konto hat Anfragelimits pro Minute, Stunde, Tag und Monat. Eine Anfrage, die ein Limit
überschreitet, wird mit `429 rate_limit_exceeded` und diesen Headern beantwortet:

| Header | Bedeutung |
| --- | --- |
| `Retry-After` | Sekunden, bis du es erneut versuchen darfst. |
| `x-ratelimit-limit` | Das Limit, das du erreicht hast. |
| `x-ratelimit-remaining` | `0`. |
| `x-ratelimit-reset` | Wann das Zeitfenster zurückgesetzt wird, als ISO-8601-Zeitpunkt. |

Auch eine sehr kurze Anfragespitze kann ein `429` ohne diese Header auslösen: Warte ein paar
Sekunden, bevor du es erneut versuchst. Die Konto-Endpunkte (`/v1/key`, `/v1/plan`,
`/v1/balance`) und `/v1/messages/count_tokens` werden nicht auf die Limits angerechnet.

## Erneute Versuche

- Wiederhole Anfragen bei `429`, `5xx` und `request_timeout`; halte dich an `Retry-After`, wenn
  der Header vorhanden ist, und nutze sonst exponentiellen Backoff.
- Wiederhole Anfragen bei `400`, `401`, `403` und `404` nicht unverändert: Dieselbe Anfrage
  scheitert auf dieselbe Weise.
- `plan_limit_reached` tritt nicht mehr auf, sobald das Zeitfenster zurückgesetzt wird, du dein
  Guthaben auflädst oder du **Guthaben-Modelle nutzen** in der **Übersicht** des Dashboards
  einschaltest.

## Hinweise neben einer Antwort

Eine erfolgreiche Antwort kann `opencheese_notifications` enthalten, eine Liste von Hinweisen zu
deinem Konto, etwa zu einem Zeitfenster deines Tarifs, das bald abläuft. Sie ändern die Antwort
nicht.
