Dies ist ein Fehler bei der Nachrichtenreihenfolge, kein Tool-Fehler. Claude verlangt, dass jeder tool_use-Block in einem Assistant-Turn durch einen tool_result-Block im unmittelbar folgenden User-Turn beantwortet wird — dieselben IDs, nichts dazwischen. Ihre Schleife hat wahrscheinlich einen Eintrag verworfen, weil das Tool einen Fehler ausgelöst hat.
Der Fehler
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "messages.1: tool_use ids were found without tool_result blocks immediately after: toolu_01A... Each tool_use block must have a corresponding tool_result block in the next message."
}
}Ursachen und Lösungen im Überblick
| Ursache | Lösung |
|---|---|
| Ihr Tool hat einen Fehler ausgelöst, daher wurde nichts angehängt | Senden Sie trotzdem einen tool_result mit is_error: true und dem Fehlertext. |
| Der tool_result wurde in einer späteren Nachricht eingefügt | Er muss die unmittelbar nächste Nachricht sein — kein Assistant- oder User-Turn dazwischen. |
| tool_use_id stimmt nicht überein | Geben Sie exakt die ID aus dem tool_use-Block zurück; generieren Sie sie nicht neu. |
| Der Verlauf wurde mitten im Roundtrip gekürzt | Kürzen Sie nur an vollständigen Tool-Roundtrips, niemals zwischen den beiden Hälften eines einzelnen Roundtrips. |
Die Invariante in einem Satz
Jeder tool_use-Block in einem Assistant-Turn benötigt genau einen tool_result-Block in der unmittelbar folgenden User-Nachricht mit derselben tool_use_id. Mehrere tool_use-Blöcke in einem Turn benötigen mehrere tool_result-Blöcke in dieser einen nächsten Nachricht. Zwischen den beiden Turns darf nichts stehen.
Antworten Sie immer, auch wenn das Tool fehlgeschlagen ist
Das Modell kann mit einem fehlgeschlagenen Tool problemlos umgehen; mit einem fehlenden Tool nicht. Wenn Sie den Fehler als tool_result zurückgeben, bleibt die Unterhaltung gültig und führt meist zu einer sinnvollen Wiederherstellung statt zu einem 400-Fehler.
results = []
for block in (b for b in resp.content if b.type == "tool_use"):
try:
out = run_tool(block.name, block.input)
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": str(out),
})
except Exception as e:
# A failed tool still owes the model an answer.
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": f"Tool failed: {e}",
"is_error": True,
})
messages.append({"role": "assistant", "content": resp.content})
messages.append({"role": "user", "content": results})Validieren Sie die letzten beiden Turns vor dem Senden
Ein Dutzend Assertionszeilen erkennen den Fehler am Aufrufort statt als 400 aus dem Netzwerk — gehen Sie die tool_use-IDs des Assistant-Turns durch und bestätigen Sie, dass der nächste User-Turn alle beantwortet.
def check_pairs(messages):
for i, m in enumerate(messages):
if m["role"] != "assistant" or not isinstance(m.get("content"), list):
continue
ids = {b.get("id") for b in m["content"]
if isinstance(b, dict) and b.get("type") == "tool_use"}
if not ids:
continue
nxt = messages[i + 1] if i + 1 < len(messages) else None
answered = {b.get("tool_use_id") for b in (nxt or {}).get("content", [])
if isinstance(b, dict) and b.get("type") == "tool_result"}
missing = ids - answered
assert not missing, f"message {i}: unanswered tool_use {missing}"Kürzen Sie den Verlauf an Roundtrip-Grenzen
Eine Kürzung des Kontextfensters nach Nachrichtenanzahl wird irgendwann zwischen tool_use und tool_result schneiden. Behandeln Sie das Paar bei der Entscheidung, was verworfen wird, als eine unteilbare Einheit.
Wenn Sie Kunavo verwenden
Dies liegt an Ihrer Payload, und Kunavo kaschiert es nicht: 400 steht auf der Liste der nicht wiederholbaren Fehler. Ein fehlerhafter Tool-Roundtrip schlägt daher einmal fehl, statt durch einen zweiten Upstream-Roundtrip zusätzliche Latenz zu verursachen, um denselben Fehler zu erreichen; die abgelehnte Anfrage wird mit Kosten von null erfasst. Unter /v1/messages sprechen Sie direkt das Messages-Protokoll an, daher werden tools-, tool_use- und tool_result-Blöcke weitergeleitet und nicht übersetzt. Dort kommt 400 als invalid_request_error zurück, wie bei Anthropics API, mit dem Nachrichtentext des Upstreams gefolgt von dessen eigener Request-ID und ohne request_id-Feld; bis zum 24. September 2026 lautete der Typ api_error. Wenn Ihre Logs weiter zurückreichen, verzweigen Sie daher anhand des HTTP-Status und des Nachrichtentexts.
Häufig gestellte Fragen
Kann ich den tool_use-Turn einfach verwerfen, statt ihn zu beantworten?
Ja, wenn Sie den gesamten Assistant-Turn verwerfen. Ungültig ist, den tool_use beizubehalten und seinen tool_result wegzulassen.
Gilt dieselbe Regel für den OpenAI-kompatiblen Endpunkt?
Dieselbe Zuordnung ist erforderlich, nur anders bezeichnet — tool_calls in der Assistant-Nachricht, danach pro Aufruf eine Nachricht mit role: "tool" und tool_call_id.
Kostet eine abgelehnte Anfrage etwas?
Bei Kunavo nein. Fehlgeschlagene Anfragen werden mit Kosten von null erfasst und erreichen niemals einen kostenpflichtigen Upstream-Aufruf.
Verwandte Anleitungen
- model_not_found / 404 — Modellbenennung bei Claude, Gemini und Gateways
- Claude API 429 rate_limit_error — Ursachen und eine dauerhafte Lösung
Weitere Informationen zur Fehlersemantik finden Sie unter Fehlerreferenz; einen Schlüssel erhalten Sie in einer Minute über Registrierung und die Authentifizierungsanleitung.