Fehler einer Embedding API sind schwerer zu bemerken als bei Chat-Schnittstellen. Scheitert eine Chat-Anfrage, sieht der Nutzer es sofort. Embeddings laufen dagegen oft in Offline-Indexpipelines; ein 400 oder 429 führt vielleicht nur dazu, dass in einem Batch einige Dokumentzeilen fehlen. Entdeckt wird das erst Tage später, wenn die Retrieval-Qualität sinkt.

Beginnen Sie die Fehlersuche nicht mit „anderes Modell probieren“. Die Reihenfolge lautet: zuerst Statuscode und Fehlertyp, dann Request-Größe und Batch, zuletzt den Vektor selbst prüfen. Im Folgenden werden 401, 413, 429, Dimensionsmismatch und leere Vektoren getrennt behandelt, ergänzt um wiederverwendbaren Code.

Hinweis: OpenAI, DeepSeek und weitere Namen sind Marken der jeweiligen Rechteinhaber. Die Schnittstellenlimits entstammen beispielhaft der öffentlichen Dokumentation der OpenAI-Embeddings-Schnittstelle: maximal 8192 Token pro Eingabe, höchstens 2048 Elemente pro Anfrage und insgesamt höchstens 300.000 Token pro Anfrage. Andere Anbieter und Gateways können abweichen; maßgeblich ist die jeweilige Dokumentation.

1. Zuerst nach Fehlertyp gruppieren

Embedding-Fehler lassen sich grob in sieben Klassen einteilen. Erst einordnen, dann entscheiden, ob Code, Batch oder Daten geändert werden müssen.

Symptom Typischer Statuscode Häufige Ursache Zuerst prüfen
Authentifizierung fehlgeschlagen 401 oder 403 Falscher Key, fehlender Header, deaktivierter Key, falsche Rechte oder Region Authorization-Header und Herkunft des Keys
Eingabe zu lang 400 oder 413 Einzeltext über Modelllimit, Request über Gateway-Limit Token pro Text und Byte-Größe des Requests
Batch zu groß 400 oder 413 Anzahl oder Gesamt-Token über Limit Anzahl und Gesamt-Token
Drosselung 429 Plötzlicher Parallelitätsanstieg, geteilter Key, Retry-Sturm Rate, Parallelität und Retry-After
Upstream-Störung 5xx Überlastung oder temporärer Ausfall Request-ID und Zeitpunkt
Dimensionsmismatch 400 oder Client-Ausnahme Modellwechsel, geänderte Dimension, fester Index Modellname, Dimension und Indexkonfiguration
Leerer Vektor oder NaN 200, aber anomal Leerer Text, Parserfehler, Division durch null bei Normalisierung Vektorlänge, endliche Werte und Norm

Im Log mindestens Zeitpunkt, Statuscode, Fehlertyp, Modellname, Batch-Größe, Laufzeit und Request-ID festhalten. Den vollständigen Key nicht loggen und den Originaltext nicht in Gänze ausgeben.

2. 401 und 403: zuerst den Request-Header prüfen

401 bedeutet, dass die Identität nicht bestätigt wurde; 403 bedeutet, dass die Identität bestätigt ist, aber der Zugriff fehlt. In beiden Fällen zunächst nicht die Modellfähigkeit verdächtigen.

Häufige Ursachen: Der Header lautet nicht Authorization: Bearer <key>; der Key enthält führende oder nachfolgende Leerzeichen oder Anführungszeichen; Umgebungsvariablen wurden nicht geladen; der Key wurde gelöscht oder deaktiviert; Base URL und Key gehören nicht zur selben Plattform; ein Reverse-Proxy verwirft den Authentifizierungsheader. Bei 403 zusätzlich Modellrechte, Projektrechte, Region und IP-Whitelist prüfen.

Senden Sie zuerst eine minimale curl-Anfrage mit kurzer Eingabe und bestätigen Sie 200. Danach schrittweise Batch, Parallelität und Proxy wieder hinzufügen. Bei weiterhin 401 einen neuen Key zum Vergleich verwenden, um Key und Gateway-Konfiguration zu unterscheiden.

3. 413 und überlange Eingaben: wessen Limit greift?

413 bedeutet meist, dass der Request-Body zu groß ist. Das kann ein Gateway- oder Reverse-Proxy-Limit sein und muss nicht vom Modell stammen. Das Eingabelimit des Modells kann dagegen 400 zurückgeben. Beide Fälle sind getrennt zu betrachten.

Bei den OpenAI-Embeddings beispielsweise: maximal 8192 Token pro Eingabe, höchstens 2048 Elemente im Array und insgesamt höchstens 300.000 Token pro Anfrage. Eine Überschreitung der Gesamtsumme führt üblicherweise zu 400, auch wenn kein Einzeltext zu lang ist. Ein Gateway kann zusätzlich eine Größenbegrenzung des Request-Bodys haben und 413 zurückgeben, bevor die Anfrage das Modell erreicht.

Bei überlangen Eingaben in dieser Reihenfolge vorgehen:

  1. Token mit dem Tokenizer des jeweiligen Modells zählen, nicht mit der Zeichenzahl schätzen.
  2. Nach Titel, Absatz und semantischen Grenzen chunken und zwischen den Blöcken eine kleine Überlappung lassen.
  3. Überlange Blöcke als Rückfall abschneiden und Originallänge sowie Schnittposition protokollieren.
  4. Bei Batches sowohl Elementanzahl als auch Gesamt-Token begrenzen und 10 bis 20 Prozent Reserve lassen.
  5. Bei 413 zuerst den Request aufteilen; bei 400 zuerst Einzelinhalt und Parameter prüfen.

Auch leere Strings beachten. Die OpenAI-Embeddings-Schnittstelle akzeptiert keine leere Eingabe; Absätze, die nach der Bereinigung leer sind, müssen vor dem Senden herausgefiltert werden.

4. 429: Drosselung, Parallelität und Retry-Stürme

429 bedeutet, dass Rate, Parallelität oder Kontingent überschritten sind, nicht dass das Guthaben fehlt. Häufige Auslöser: mehrere Dienste teilen einen Key, Batch-Aufgaben starten gleichzeitig zur vollen Stunde, Wiederholungen ohne Backoff, lange Anfragen belegen viel Kontingent.

Prüfen Sie zuerst den Response-Header Retry-After und warten Sie entsprechend. Fehlt er, exponentielles Backoff mit zufälligem Jitter und einer Obergrenze für Wiederholungen verwenden. Wiederholungen eignen sich nur für temporäre Fehler: 401, 403, 400 und die meisten 413 werden auch nach vielen Versuchen nicht erfolgreich.

Technisch zusätzlich sinnvoll: Keys nach Projekt trennen; Batch-Aufgaben eine Warteschlange und ein Parallelitätslimit geben; große Aufgaben zeitlich entzerren; fehlgeschlagene Datensätze in eine Dead-Letter-Queue schreiben. Drei bis fünf Wiederholungen genügen; danach alarmieren, nicht endlos wiederholen.

5. Dimensionsmismatch: der am leichtesten übersehene Fehler

Ein Dimensionsmismatch kann als Datenbankfehler erscheinen, etwa „expected dimension 1536, got 1024“, als Formfehler in numpy, als Broadcast-Fehler bei der Kosinus-Berechnung oder auch nur als plötzlich unbrauchbare Retrieval-Ergebnisse.

Häufige Ursachen: Modell gewechselt, alte Vektoren aber noch im Index; dimensions-Parameter geändert; gleichnamige Modelle haben bei unterschiedlichen Anbietern oder Versionen verschiedene Dimensionen; der Index wurde mit fester Dimension angelegt; jemand hat manuell abgeschnitten oder mit Nullen aufgefüllt. Gleiche Dimension bedeutet nicht, dass sich Vektoren mischen lassen; die Vektorräume unterschiedlicher Modelle sind nicht kompatibel.

Die Lösung ist kein Auffüllen mit Nullen, sondern ein Neuaufbau. Zu jedem Vektor Modellname, Dimension, Normalisierungsart, Distanzmaß und Chunking-Version speichern; pro Index nur Vektoren desselben Modells und derselben Version; bei einem Modellwechsel eine neue Collection anlegen, vollständig neu berechnen und erst nach Validierung umschalten. Sowohl beim Schreiben als auch beim Abfragen die Dimension prüfen.

6. Leere Vektoren, Nullvektoren und NaN

Ein Status 200 bedeutet nicht, dass die Daten brauchbar sind. Leere Vektoren, Nullvektoren und NaN machen die Ähnlichkeitsberechnung unbrauchbar, häufig ohne eine Ausnahme auszulösen.

Häufige Quellen: Text, der nach der Bereinigung nur noch Leerraum enthält; ein leeres Eingabe-Array; ein Parserfehler, durch den embedding eine leere Liste ist; ein lokales Modell, das für leere Eingaben einen Nullvektor liefert; Division durch null bei der Normalisierung; fehlerhaft dekodiertes base64.

Vor dem Einspielen mindestens drei Prüfungen: Entspricht die Länge der erwarteten Dimension? Ist jedes Element eine endliche Zahl? Ist die Vektornorm größer als ein sehr kleiner Schwellenwert? Fehlgeschlagene Datensätze in eine Quarantänetabelle mit Original-ID und Grund schreiben und nach der Korrektur erneut verarbeiten, nicht stillschweigend verwerfen.

7. Wiederverwendbarer Fehlerbehandlungscode

Das folgende Beispiel richtet sich an OpenAI-kompatible Schnittstellen und deckt Backoff bei Drosselung, Batch-Aufteilung und Vektorvalidierung ab. Ersetzen Sie Modellname, Dimension und Base URL durch Ihre Konfiguration.

import logging
import math
import random
import time

from openai import OpenAI, APIStatusError, APITimeoutError, RateLimitError

logger = logging.getLogger("embedding")
client = OpenAI(
    api_key="sk-IHR_KEY",
    base_url="https://api.zhongshangqihang.com/v1",
    max_retries=0,  # automatische SDK-Retries aus, um Überlagerung zu vermeiden
)

MODEL = "text-embedding-3-small"
EXPECTED_DIM = 1536


def vector_is_valid(vec: list[float]) -> bool:
    if len(vec) != EXPECTED_DIM:
        return False
    if not all(math.isfinite(x) for x in vec):
        return False
    norm = math.sqrt(sum(x * x for x in vec))
    return norm > 1e-6


def embed_batch(texts: list[str], attempt: int = 0) -> list[list[float] | None]:
    # Leere Texte nicht senden, None als Platzhalter für gleiche Indizes
    pairs = [(i, t) for i, t in enumerate(texts) if t and t.strip()]
    if not pairs:
        return [None] * len(texts)
    indices = [i for i, _ in pairs]
    payload = [t for _, t in pairs]

    try:
        resp = client.embeddings.create(model=MODEL, input=payload, timeout=60)
        items = sorted(resp.data, key=lambda item: item.index)
        vectors = [item.embedding for item in items]
        if len(vectors) != len(payload):
            raise ValueError(f"Anzahl ungleich: {len(vectors)} != {len(payload)}")
        for index, vec in enumerate(vectors):
            if not vector_is_valid(vec):
                raise ValueError(f"Vektor anomal: index={index}, dim={len(vec)}")
        result: list[list[float] | None] = [None] * len(texts)
        for source_index, vec in zip(indices, vectors):
            result[source_index] = vec
        return result
    except (RateLimitError, APITimeoutError) as exc:
        if attempt >= 5:
            raise
        wait = min(2 ** attempt, 30) + random.random()
        logger.warning("Drosselung oder Timeout, neuer Versuch in %.1f s: %s", wait, exc)
        time.sleep(wait)
        return embed_batch(texts, attempt + 1)
    except APIStatusError as exc:
        if exc.status_code in (400, 413) and len(texts) > 1:
            mid = len(texts) // 2
            logger.warning("Batch zu groß, Aufteilung in %d + %d", mid, len(texts) - mid)
            return embed_batch(texts[:mid], attempt) + embed_batch(texts[mid:], attempt)
        if exc.status_code >= 500 and attempt < 5:
            wait = min(2 ** attempt, 30) + random.random()
            logger.warning("Upstream-Fehler, neuer Versuch in %.1f s: %s", wait, exc)
            time.sleep(wait)
            return embed_batch(texts, attempt + 1)
        raise

Der Code enthält zwei Abwägungen. Erstens wird auch 400 aufgeteilt, weil manche Gateways „Gesamt-Token überschritten“ als 400 melden; schlägt ein einzelner Text weiterhin fehl, wird die Ausnahme weitergereicht. Zweitens erfolgt die Vektorvalidierung vor dem Schreiben, sodass fehlerhafte Daten nicht in den Index gelangen. In der Produktion sollten zusätzlich Request-ID, Anzahl der Wiederholungen und fehlgeschlagene Batches protokolliert werden.

8. Reihenfolge der Fehlersuche und Monitoring-Checkliste

Bei Embedding-Fehlern in dieser Reihenfolge vorgehen; das ist meist schneller als wiederholtes Modellwechseln:

  1. Statuscode, Fehlertyp, Request-ID, Modellname, Batch-Größe und Laufzeit erfassen.
  2. Bei 401 oder 403 zuerst Header, Key, Umgebungsvariablen und Plattform prüfen.
  3. Bei 400 zuerst Token pro Text, Gesamt-Token, leere Strings und Parameterformat prüfen.
  4. Bei 413 Byte-Größe des Requests und Gateway-Limits prüfen, Anfrage verkleinern und erneut senden.
  5. Bei 429 Retry-After beachten, Backoff und Parallelität senken, geteilte Keys prüfen.
  6. Bei 5xx begrenzt mit Backoff wiederholen; bei anhaltendem Auftreten mit Request-ID an den Anbieter wenden.
  7. Bei Status 200 mit anomalen Ergebnissen Dimension, Norm, NaN und Normalisierung prüfen.
  8. Diese Kennzahlen ins Monitoring aufnehmen und regelmäßig mit einem festen Evaluierungsset regressieren.

Das Monitoring zählt stündlich 401, 400, 413, 429 und 5xx sowie die Anzahl leerer Vektoren und Dimensionsanomalien. Steigt die Fehlerrate sprunghaft, zuerst prüfen, ob sich die Fälle auf ein Projekt, einen Key, ein Modell oder einen Dokument-Batch konzentrieren; danach über Parallelitätssenkung, Batch-Aufteilung, Rollback oder Datenkorrektur entscheiden.

Häufige Fragen

413 und 400 bedeuten beide „Eingabe zu lang“ – wo liegt der Unterschied?

413 stammt meist aus einer Größenbegrenzung von Gateway oder Reverse-Proxy; die Anfrage erreicht das Modell möglicherweise gar nicht. 400 kommt eher von Schnittstellenparametern oder Token-Limits. Prüfen Sie Response-Body und Request-ID und entscheiden Sie dann, ob der Request aufgeteilt oder der Text weiter zerlegt wird.

Ein Einzeltext liegt unter 8192 Token, trotzdem kommt 400 – warum?

Möglicherweise überschreitet die Gesamt-Tokenzahl der Anfrage das Limit, oder das Eingabe-Array hat zu viele Elemente, enthält leere Strings, falsch formatierte Parameter oder einen falschen Modellnamen. Batch verkleinern, leere Texte filtern und den Request Punkt für Punkt prüfen.

Wie viele Wiederholungen bei 429 sind sinnvoll?

Üblich sind drei bis fünf, mit exponentiellem Backoff und zufälligem Jitter, wobei Retry-After Vorrang hat. Danach in die Warteschlange oder Alarm auslösen, nicht endlos wiederholen. Bei starker Drosselung hilft eine Senkung der Parallelität mehr als zusätzliche Wiederholungen.

Lässt sich ein Dimensionsmismatch durch Auffüllen mit Nullen beheben?

Nein. Nullen verändern Richtung und Ähnlichkeit des Vektors; die Retrieval-Ergebnisse wären nicht verlässlich. Richtig ist, Modell und Dimension zu vereinheitlichen, einen neuen Index vollständig neu zu berechnen und erst nach Validierung umzuschalten.

Warum kommt Status 200, aber der Vektor ist leer?

Möglicherweise ist die Eingabe leer oder besteht nach der Bereinigung nur aus Leerraum; auch Fehler bei der Antwortanalyse, base64-Dekodierung oder lokalen Inferenz sind möglich. Vor dem Einspielen Länge, endliche Werte und Norm prüfen und Anomalien zur Untersuchung isolieren.

Nächster Schritt

Legen Sie im ZSQH AI Gateway einen Test-Key an und testen Sie mit einem kleinen Textbatch Authentifizierung, Batch-Verarbeitung, Drosselung und Vektorvalidierung, bevor Sie die Produktionspipeline anschließen. Modellverfügbarkeit und Preise stehen in der Preisübersicht.

Weiterlesen: