Reliability and Routing22. September 2026Flatkey Team

API-Fehler 400 "Text Content Blocks Must Be Non-Empty": Ursachen und 5 Lösungen

Beheben Sie den API-Fehler 400 "Text Content Blocks Must Be Non-Empty" in Anthropic-Anfragen mit Payload-Prüfungen, Sanitizer-Code, SDK-Beispielen und QA-Schritten.

API-Fehler 400 "Text Content Blocks Must Be Non-Empty": Ursachen und 5 Lösungen

API-Fehler 400 "Text Content Blocks Must Be Non-Empty": Ursachen und 5 Lösungen bedeutet, dass eine Anthropic-Messages-API-Anfrage mindestens einen Textblock enthält, dessen text-Wert leer ist. Die Anfrage mag auf Message-Ebene gültig aussehen, aber der Anbieter lehnt sie vor der Generierung ab, weil ein Text-Content-Block mindestens ein Zeichen enthalten muss.

Die schnelle Lösung ist einfach: Leere Textblöcke entfernen, Leerzeichen-allein-Benutzereingaben vor dem Erstellen der Anfrage trimmen und niemals Platzhalterblöcke wie {"type":"text","text":""} senden. Der schwierigere Teil ist herauszufinden, wo diese Blöcke eingefügt werden. In Produktiv-Apps stammen sie oft aus Chat-UI-Entwürfen, leeren Retrieval-Chunks, Markdown-Cleanern, Template-Variablen, Streaming-Transkript-Puffern oder multimodalen Adaptern, die ein Content-Array aufbauen, bevor sie wissen, ob Text vorhanden ist.

Nutzen Sie diesen Leitfaden, um den Fehler zu debuggen, den Request-Builder zu korrigieren und eine Preflight-Prüfung hinzuzufügen, damit derselbe 400er-Fehler nicht erneut ausgeliefert wird.

Kurzantwort: API-Fehler 400 "Text Content Blocks Must Be Non-Empty"

Anthropic akzeptiert den Nachrichten-content entweder als einfachen String oder als Array typisierter Content-Blöcke. In der Array-Form sieht ein Textblock so aus:

{
  "type": "text",
  "text": "Fassen Sie dieses Support-Ticket zusammen."
}

Dies schlägt fehl, weil das text-Feld leer ist:

{
  "type": "text",
  "text": ""
}

Dies kann in der Praxis auch fehlschlagen, wenn Ihre App einen Wert, der nur aus Leerzeichen besteht, in eine leere Zeichenfolge normalisiert:

{
  "type": "text",
  "text": "   "
}

Die sicherste Regel lautet:

  1. Textwerte vor dem Erstellen der Anthropic-Anfrage trimmen.
  2. Textblöcke entfernen, deren getrimmter Text leer ist.
  3. Wenn eine Nachricht keine verbleibenden Content-Blöcke hat, diese Nachricht nicht senden.
  4. Die bereinigte Payload-Struktur protokollieren, ohne privaten Prompt-Text zu loggen.
  5. Einen Unit-Test für leere Zeichenfolgen, Leerzeichen, Nullwerte und leere Retrieval-Ergebnisse hinzufügen.

Das ist die praktische Lösung für API-Fehler 400 "Text Content Blocks Must Be Non-Empty": Ursachen und 5 Lösungen.

Warum dieser Fehler auftritt

Die Messages API von Anthropic verwendet strukturierte Gesprächsrunden. Jede Eingabe-Nachricht hat eine role und content. Der content-Wert kann ein einzelner String sein oder ein Array von Blöcken wie Text- und Bildblöcken. Die offizielle Messages-API-Referenz beschreibt String-Content als Kurzform für einen Textblock und führt text auf einem Textblock mit minLength: 1 auf.

Die Fehlerreferenz von Anthropic stuft HTTP 400 als invalid_request_error ein: ein Problem mit dem Anfrageformat oder dem Inhalt. Dies ist also kein Rate-Limit-, Authentifizierungs-, Provider-Ausfall- oder Modellqualitätsproblem. Es ist ein Problem bei der Anfragevalidierung.

Für KI-Produktteams ist die operative Lektion wichtig: Das erneute Senden derselben Anfrage hilft nicht. Sie müssen die Nutzlast vor dem erneuten Versuch korrigieren.

Fünf häufige Ursachen

1. Leere Chat-Eingabe erreicht die API

Der häufigste Pfad ist ein Chat-Composer, der es einem Benutzer erlaubt, einen leeren Entwurf oder einen Entwurf zu senden, der nach dem Trimmen leer wird.

Ungültige Anfrage:

{
  "model": "claude-sonnet-5",
  "max_tokens": 512,
  "messages": [
    {
      "role": "user",
      "content": [
        { "type": "text", "text": "" }
      ]
    }
  ]
}

Beheben Sie das vor dem API-Aufruf:

const input = userInput.trim();

if (!input) {
  throw new Error("Message text is required before calling Anthropic.");
}

const messages = [
  {
    role: "user",
    content: input
  }
];

Verwenden Sie in der UI eine Validierungsnachricht auf Produktebene. Lassen Sie das Backend keinen leeren Prompt von einem Provider-400 entdecken.

2. Abruf fügt leere Chunks hinzu

RAG-Pipelines ordnen abgerufene Dokumente oft in Prompt-Abschnitte ein. Wenn ein Abruf-Ergebnis ein leeres Snippet, einen entfernten HTML-Body oder ein fehlgeschlagenes OCR-Feld hat, kann der Adapter einen leeren Textblock erzeugen.

Schlechter Adapter:

const content = retrievedDocs.map((doc) => ({
  type: "text",
  text: doc.cleanedText
}));

Sichererer Adapter:

const content = retrievedDocs
  .map((doc) => (doc.cleanedText ?? "").trim())
  .filter(Boolean)
  .map((text) => ({ type: "text", text }));

Wenn das Modell Quellkontext benötigt, behalten Sie außerdem eine Anzahl der verworfenen Chunks. Wenn jeder abgerufene Chunk leer ist, stoppen Sie und geben Sie statt eines leeren Prompts einen Abruffehler zurück.

3. Vorlagenvariablen werden zu nichts gerendert

Prompt-Vorlagen sind eine weitere häufige Ursache für API-Fehler 400 "Text Content Blocks Must Be Non-Empty": Ursachen und 5 Lösungen. Eine Vorlage kann im Code befüllt aussehen, aber zur Laufzeit einen leeren Abschnitt rendern:

const prompt = `
Kundennachricht:
${customerMessage}
`;

Wenn customerMessage undefined, null oder nach der Bereinigung leer ist, kann der endgültige Prompt unbrauchbar oder leer sein.

Verwenden Sie explizite Pflichtfelder:

function requiredText(name: string, value: unknown): string {
  const text = String(value ?? "").trim();
  if (!text) {
    throw new Error(`Missing required prompt field: ${name}`);
  }
  return text;
}

const prompt = `Kundennachricht:\n${requiredText("customerMessage", customerMessage)}`;

So wird ein vager Provider-Fehler in einen lokalen Anwendungsfehler mit dem Namen des fehlenden Felds umgewandelt.

4. Multimodale Builder fügen einen Platzhalter-Textblock hinzu

Teams, die Bild-und-Text-Workflows bauen, initialisieren Inhaltsarrays manchmal mit einem Platzhalter-Textblock und füllen ihn später. Wenn der Text optional ist und kein Text ankommt, bleibt der Platzhalter leer.

Schlechtes Muster:

[
  { "type": "text", "text": "" },
  {
    "type": "image",
    "source": {
      "type": "base64",
      "media_type": "image/png",
      "data": "..."
    }
  }
]

Sichereres Muster:

const content: Array<Record<string, unknown>> = [];

const instruction = optionalInstruction.trim();
if (instruction) {
  content.push({ type: "text", text: instruction });
}

content.push({
  type: "image",
  source: imageSource
});

Erstellen Sie Blöcke nur, wenn der entsprechende Inhalt vorhanden ist. Verwenden Sie keine leeren Textblöcke als Trennzeichen.

5. Komprimierung des Nachrichtenverlaufs hinterlässt leere Turns

Länger laufende Assistenten komprimieren oder fassen frühere Turns oft zusammen. Wenn der Komprimierungsschritt einen Nachrichteninhalt entfernt, aber den Turn im Verlauf belässt, kann Ihre Anfrage eine leere Assistenten- oder Benutzernachricht enthalten.

Beispiel für einen Fehler:

{
  "role": "assistant",
  "content": [
    { "type": "text", "text": "" }
  ]
}

Verwenden Sie vor jedem Aufruf einen Verlaufs-Sanitizer:

type TextBlock = { type: "text"; text: string };
type Message = { role: "user" | "assistant"; content: string | TextBlock[] };

function sanitizeMessages(messages: Message[]): Message[] {
  return messages.flatMap((message) => {
    if (typeof message.content === "string") {
      const text = message.content.trim();
      return text ? [{ ...message, content: text }] : [];
    }

    const content = message.content
      .map((block) => ({ ...block, text: block.text.trim() }))
      .filter((block) => block.type !== "text" || block.text.length > 0);

    return content.length ? [{ ...message, content }] : [];
  });
}

Stellen Sie dann sicher, dass vor dem Aufruf der API mindestens eine Nachricht übrig bleibt.

Ein kopierbarer Preflight-Validator

Verwenden Sie einen Preflight-Validator für Anfragen nahe der finalen Netzwerkgrenze. Dadurch werden leere Blöcke erfasst, selbst wenn eine nachgelagerte UI-, Template-, RAG- oder Speicherkomponente sie übersieht.

type ContentBlock =
  | { type: "text"; text?: unknown }
  | { type: string; [key: string]: unknown };

type AnthropicMessage = {
  role: "user" | "assistant";
  content: string | ContentBlock[];
};

export function validateAnthropicMessages(messages: AnthropicMessage[]) {
  const cleaned = messages.flatMap((message, messageIndex) => {
    if (typeof message.content === "string") {
      const text = message.content.trim();
      return text ? [{ ...message, content: text }] : [];
    }

    const content = message.content.flatMap((block, blockIndex) => {
      if (block.type !== "text") return [block];

      const text = String(block.text ?? "").trim();
      if (!text) {
        console.warn("Leeren Anthropic-Textblock verworfen", {
          messageIndex,
          blockIndex
        });
        return [];
      }

      return [{ ...block, text }];
    });

    return content.length ? [{ ...message, content }] : [];
  });

  if (!cleaned.length) {
    throw new Error("Die Anthropic-Anfrage enthält keinen nicht leeren Nachrichteninhalt.");
  }

  return cleaned;
}

Dies ist absichtlich konservativ. Es entfernt leere Textblöcke, behält Nicht-Text-Blöcke bei, entfernt leere Nachrichten und verweigert den Aufruf des Modells, wenn kein nutzbarer Nachrichteninhalt übrig bleibt.

Python-Version

Wenn Ihr Backend Python ist, verwenden Sie dieselbe Grenzprüfung:

def sanitize_anthropic_messages(messages):
    cleaned_messages = []

    for message_index, message in enumerate(messages):
        content = message.get("content")

        if isinstance(content, str):
            text = content.strip()
            if text:
                cleaned_messages.append({**message, "content": text})
            continue

        if isinstance(content, list):
            cleaned_blocks = []

            for block_index, block in enumerate(content):
                if block.get("type") != "text":
                    cleaned_blocks.append(block)
                    continue

                text = str(block.get("text") or "").strip()
                if text:
                    cleaned_blocks.append({**block, "text": text})
                else:
                    print(
                        "Leerer Anthropic-Textblock verworfen",
                        {"message_index": message_index, "block_index": block_index},
                    )

            if cleaned_blocks:
                cleaned_messages.append({**message, "content": cleaned_blocks})

    if not cleaned_messages:
        raise ValueError("Anthropic-Anfrage hat keinen nicht-leeren Nachrichteninhalt.")

    return cleaned_messages

Behalten Sie die Struktur der Protokoll-Metadaten bei. Protokollieren Sie keine rohen Benutzer-Prompts, Kundendokumente oder privaten Retrieval-Text, es sei denn, Ihre Datenschutzrichtlinie und Ihr Debugging-Workflow erlauben dies ausdrücklich.

Debugging-Checkliste

Wenn API-Fehler 400 "Text Content Blocks Must Be Non-Empty": Ursachen und 5 Lösungen in der Produktion auftritt, debuggen Sie in dieser Reihenfolge:

Prüfung Was zu prüfen ist Lösung
UI-Eingabe Leerer oder nur aus Leerzeichen bestehender Benutzerdraft Absenden blockieren, bis getrimmter Text vorhanden ist
Prompt-Vorlage Erforderliche Variable wird leer gerendert Erforderliche Felder nach Namen validieren
RAG-Chunks Leeres cleanedText, OCR-Ergebnis oder Markdown-Body Chunks filtern und fehlschlagen, wenn der gesamte Kontext leer ist
Multimodale Anfrage Platzhalter-Textblock vor einem Bild-/Dateiblock Textblöcke nur hinzufügen, wenn Text vorhanden ist
Verlaufs-Komprimierung Leere Benutzer- oder Assistenten-Turns nach der Zusammenfassung Die finale Nachrichtenliste bereinigen
Netzwerkgrenze Das finale Payload enthält weiterhin text: "" Einen Preflight-Validator und Unit-Tests hinzufügen

Das finale Netzwerk-Payload ist die maßgebliche Quelle. Wenn Ihre Logs keinen leeren Textblock zeigen, bestätigen Sie, dass das SDK null, undefined oder ein leeres Array nicht während der Serialisierung in einen Textblock umwandelt.

Zu ergänzende Unit-Tests

Fügen Sie mindestens Tests für diese Fälle hinzu:

const cases = [
  { name: "leerer einfacher String", content: "" },
  { name: "einfacher Whitespace-String", content: "   " },
  { name: "leerer Textblock", content: [{ type: "text", text: "" }] },
  { name: "fehlendes Textfeld", content: [{ type: "text" }] },
  { name: "null-Textfeld", content: [{ type: "text", text: null }] },
  { name: "gültiger Textblock", content: [{ type: "text", text: "Hello" }] }
];

Ihr erwartetes Verhalten sollte eindeutig sein:

  • Leere textbasierte Nachrichten werden lokal entfernt oder abgelehnt.
  • Gültiger Text bleibt erhalten, wobei führende und nachgestellte Leerzeichen entfernt werden.
  • Nicht-Text-Inhaltsblöcke bleiben erhalten.
  • Eine Anfrage ohne nutzbaren Inhalt löst vor dem Aufruf von Anthropic einen Fehler aus.
  • Der ausgelöste Fehler identifiziert die Grenze Ihrer Anwendung, nicht nur die Antwort des Anbieters.

Wo Flatkey hineinpasst

Wenn Ihr Team Claude-Traffic über Flatkey leitet, halten Sie die gleiche Anthropic-Payload-Disziplin ein. Der Anthropic-SDK-Leitfaden von Flatkey zeigt base_url="https://router.flatkey.ai" für den Anthropic-SDK-Pfad, während die OpenAI-kompatible API https://router.flatkey.ai/v1 für Chat-Completions-artige Anfragen verwendet. Verwenden Sie die Route, die zu Ihrem Client und Ihrer Endpoint-Struktur passt.

Für diesen Fehler ist Flatkey vor allem als Betriebsebene rund um die Behebung nützlich:

  • Behalten Sie einen einzigen Ort, um zu prüfen, ob die Anfrage das Gateway erreicht hat.
  • Vergleichen Sie nach einem erfolgreichen Retry den Anfrage-Status und die Nutzungsnachweise.
  • Halten Sie einen kleinen Smoke-Test getrennt vom vollständigen Prompt des Benutzers.
  • Vermeiden Sie es, Anthropic-formatierte Anfragen und OpenAI-kompatible Anfragen im selben Adapter zu mischen.

Wenn Sie eine Route für eine Claude-Workload wählen, lesen Sie Claude API Proxy vs Multi-Model Router. Wenn Sie standardisieren möchten, wie Ingenieure ihren ersten sicheren Aufruf machen, behalten Sie den Flatkey API quickstart in der Nähe. Für breitere Produktionsprüfungen kombinieren Sie dies mit AI routing API metrics und dem AI model catalog guide.

Was Sie nicht tun sollten

Lösen Sie API-Fehler 400 "Text Content Blocks Must Be Non-Empty": Ursachen und 5 Lösungen nicht mit blindem Wiederholen. Der Anbieter sagt Ihnen, dass die Anfrage fehlerhaft formatiert ist.

Vermeiden Sie diese Anti-Patterns:

Anti-Pattern Warum es scheitert
Dasselbe Payload erneut senden Ein deterministischer Validierungsfehler wird weiterhin fehlschlagen
Leeren Text durch "." ersetzen Es verschleiert upstreamen Datenverlust und kann das Modellverhalten ändern
Leere Assistant-Antworten senden Es verschmutzt den Verlauf und kann die Antwortfortsetzung unterbrechen
Vollständige Prompts zum Debuggen protokollieren Es kann Kundendaten oder Geheimnisse offenlegen
Nur das UI reparieren Backend-Jobs, RAG, Webhooks und Agent-Loops können weiterhin leere Blöcke erzeugen

Die dauerhafte Lösung besteht darin, Inhalte sowohl an der Produzenten-Grenze als auch an der endgültigen API-Grenze zu validieren.

FAQ

Ist das ein Anthropic-Ausfall?

Nein. Ein 400 invalid_request_error für leere Textblöcke ist ein Problem bei der Request-Validierung. Überprüfen Sie das Payload, das Ihre Anwendung sendet.

Kann ich einen einfachen String statt eines Textblock-Arrays senden?

Ja. Die Messages API von Anthropic erlaubt es, dass content einer Nachricht ein String ist, und die Dokumentation beschreibt das als Kurzform für einen einzelnen Textblock. Verwenden Sie einen String, wenn Sie nur einfachen Text benötigen. Verwenden Sie ein Array, wenn Sie mehrere Blöcke oder multimodale Eingaben benötigen.

Sollte Leerraum als nicht leer zählen?

Behandeln Sie Text, der nur aus Leerraum besteht, in Ihrem eigenen Validator als leer. Selbst wenn ein Anbieter ihn akzeptiert hätte, ist er kein nützlicher Prompt-Inhalt und weist meist auf einen Fehler in der UI, Vorlage oder Retrieval hin.

Können Nachrichten nur mit Bildern funktionieren?

Eine multimodale Anfrage benötigt keinen leeren Text-Platzhalter. Wenn Sie einen Bildblock einfügen, erstellen Sie den Bildblock direkt und fügen Sie nur dann einen Textblock hinzu, wenn Sie echten Anweisungstext haben.

Was sollte ich protokollieren?

Protokollieren Sie die Anzahl der Nachrichten, die Typen der Content-Blöcke, die Block-Indexe, das Modell, den Endpunkt, die Route, den Statuscode und, falls verfügbar, die Request-ID. Vermeiden Sie es, den vollständigen Prompt-Text zu protokollieren, sofern die Datenschutzregeln Ihres Teams dies nicht erlauben.

Offizielle Referenzen