API: Fehler, Status-Codes & Limits
Vollständige Referenz der HTTP-Status-Codes, des OpenAI-kompatiblen Fehlerformats, der Budget- und Größenlimits der öffentlichen Kasimir-API.
Die öffentliche Kasimir-API ist OpenAI-kompatibel: Fehler werden im selben JSON-Format wie bei OpenAI zurückgegeben, damit handelsübliche OpenAI-SDKs sie ganz normal als Exception bzw. Fehlerobjekt anzeigen. Dieses Kapitel beschreibt exakt, welche Status-Codes die Endpunkte unter /api/v1/* werfen, was sie bedeuten, wie das Fehlerobjekt aufgebaut ist und welche harten Grenzen (Budget, Payload-Größen) gelten – alles direkt aus dem Quellcode abgeleitet.
Fehlerformat
Alle Fehler der OpenAI-kompatiblen Endpunkte (POST /v1/chat/completions, POST /v1/embeddings, GET /v1/models) werden von server/utils/apiError.ts (toOpenaiError) in das OpenAI-Schema übersetzt:
{
"error": {
"message": "Model \"foo\" not found",
"type": "not_found_error",
"code": null
}
}Feld | Inhalt |
|---|---|
| Klartext aus |
| Aus dem Status-Code abgeleitet (siehe Tabelle unten). |
| Immer |
Der type-Wert wird strikt aus dem Status-Code gemappt:
Status |
|
|---|---|
|
|
|
|
|
|
|
|
|
|
alle anderen (z. B. |
|
Der HTTP-Status-Code und der type im Body sind immer konsistent – toOpenaiError setzt beide aus derselben Quelle (setResponseStatus + Mapping).
Sonderfall Streaming: Tritt ein Fehler nachdem der SSE-Stream begonnen hat auf (started = true in chat/completions.post.ts), kann er nicht mehr in ein OpenAI-Fehlerobjekt umgeformt werden. Der Stream bricht dann einfach ab. Behandle einen unerwartet endenden Stream client-seitig als Fehler.
Status-Codes im Detail
Die folgende Tabelle listet jeden Status-Code, der von der /api/v1/*-Oberfläche geworfen wird, samt auslösender Code-Stelle und Bedeutung.
Status |
| Wann | Quelle |
|---|---|---|---|
|
| Body verletzt das Zod-Schema ( |
|
|
| Kein |
|
|
| Monatsbudget der Firma erreicht/überschritten |
|
|
| Schlüssel besitzt nicht den benötigten Scope (z. B. |
|
|
| Modell-Slug unbekannt/inaktiv/falsche Purpose; Modell per |
|
|
| Nur Schlüssel-Verwaltung: Name bereits vergeben ( |
|
|
| Upstream (LiteLLM) liefert keinen Erfolg und keinen eigenen Status – Fallback ` | |
|
| Upstream-Fehler mit eigenem Status ( |
|
|
| Jeder nicht klassifizierte Fehler (Default in |
|
402 bedeutet nicht „pro Anfrage zu teuer“, sondern „Firmen-Monatsbudget aufgebraucht“. Der Verbrauch summiert API- und In-App-Chat-Nutzung des laufenden Kalendermonats (RPC company_month_spend_eur).
401 – Authentifizierung
authenticateApiKey (server/utils/apiKeyAuth.ts) wirft 401 in drei Fällen:
Kein Bearer-Token –
message: "Missing bearer token"Ungültiger Schlüssel – Hash nicht in
company_api_keysgefunden –message: "Invalid API key"Widerrufener Schlüssel –
revoked_atgesetzt –message: "API key revoked"
Schlüssel haben das Format kasi_<32 Zeichen> und werden nur als SHA-256-Hash gespeichert; der Klartext ist einmalig bei der Erstellung sichtbar.
402 – Budget
assertCompanyBudget (server/utils/apiUsage.ts) prüft vor dem Forwarding an den Upstream:
companies.monthly_budget_euristnulloder≤ 0→ unbegrenzt, kein Limit.Andernfalls wird der Monatsverbrauch via
company_month_spend_eurermittelt. Istused ≥ budget, kommt402mit z. B.Monthly budget of €100.00 reached (used €100.42).
Fail-open bei Infrastruktur-Fehlern: Schlägt die Budget-Abfrage selbst fehl (DB-Glitch), wird die Anfrage durchgelassen statt blockiert – nur ein echtes 402 (Budget überschritten) wird durchgereicht. Eine flaky Abfrage sperrt einen Kunden also nie aus.
403 – Berechtigung
Zwei unabhängige Ursachen:
Fehlender Scope –
authenticateApiKey(event, ['chat:write'])wirftMissing scope: chat:write, wenn dem Schlüssel der nötige Scope fehlt.GET /v1/modelserfordert keinen speziellen Scope;chat/completionsundembeddingserfordernchat:write.Non-EU-Gate –
resolveApiModelwirft403, wenn das Modelleu_hosted = falseist undcompanies.allow_non_eu_modelsnichttrueist:Model "…" is not enabled for your organization (non-EU model).
404 – Nicht gefunden
resolveApiModel (Chat/Embeddings) bzw. der Assistant-Handler werfen 404, wenn:
der Slug in
modelsnicht existiert, inaktiv ist oder eine andere Purpose hat (chatvs.embedding),der Slug in
companies.disabled_model_slugssteht (wird bewusst als „not found“ getarnt),ein Assistant (
/v1/assistants/{id}/run) nicht existiert oder einer fremden Firma gehört (Tenant-Isolation),das Assistant-Modell nicht existiert/inaktiv ist –
Model "…" not found or inactive.
502 / durchgereichte 5xx – Upstream
Antwortet die LiteLLM-Schicht nicht mit 2xx (oder fehlt der Body beim Streaming), wirft Kasimir res.status || 502 mit Upstream error: <erste 200 Zeichen der Upstream-Antwort>. Bei 502 ist die Ursache typischerweise, dass der Upstream gar keinen verwertbaren Status lieferte.
Limits
In Phase 1 gibt es keine Rate-Limits und keine 429-Antworten – pro-Key-Spend-Caps und pro-Key-Rate-Limits sind ausdrücklich auf Phase 2 verschoben. Das einzige durchgesetzte „Mengen“-Limit ist das Firmen-Monatsbudget (siehe 402). Daneben gelten Payload-Schemagrenzen:
Limit | Wert | Endpunkt | Quelle |
|---|---|---|---|
Monatsbudget (Firma) |
| chat, embeddings |
|
| mindestens 1 Nachricht |
| Zod |
Assistant- | 1 bis 50 Nachrichten |
| Zod |
Assistant- | 0 bis 2 |
| Zod |
Assistant- | 1 bis 32000 (Integer) |
| Zod |
Rate-Limit / | nicht vorhanden (Phase 2) | – | Spec §Non-Goals |
Für chat/completions und embeddings werden temperature, max_tokens, tools, response_format usw. nicht von Kasimir validiert, sondern verbatim an den Upstream durchgereicht (.passthrough()). Ungültige Werte führen daher zu einem durchgereichten Upstream-Fehler (z. B. 400/5xx), nicht zu einem Kasimir-400.
Fehlerbehandlung (Empfehlungen)
Status | Reaktion |
|---|---|
| Request-Body korrigieren; nicht erneut versuchen (deterministisch). |
| Schlüssel prüfen/erneuern; widerrufene Schlüssel in der App neu anlegen. |
| Budget in Administration → … anheben oder bis zum Monatswechsel warten; Retry zwecklos. |
| Scope des Schlüssels prüfen bzw. EU-Gate ( |
| Modell-Slug gegen |
| Tritt aktuell nicht auf – keine Behandlung nötig (kommt erst in Phase 2). |
| Transienter Upstream-Fehler – mit Backoff erneut versuchen. |
Da das Fehlerformat OpenAI-konform ist, fängt jedes OpenAI-SDK diese Fälle automatisch ab – z. B. wirft das Python-SDK AuthenticationError (401), PermissionDeniedError (403), NotFoundError (404) und APIStatusError (5xx). Auf error.message lässt sich der konkrete Grund auslesen.