Ini adalah error pengurutan pesan, bukan error tools. Claude mengharuskan setiap blok tool_use dalam turn assistant dijawab oleh blok tool_result pada turn user yang langsung berikutnya — id yang sama, tanpa apa pun di antaranya. Loop Anda menghilangkan salah satunya, biasanya karena tool mengalami error.
Error
{
"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."
}
}Ringkasan penyebab dan perbaikan
| Penyebab | Perbaikan |
|---|---|
| Tool Anda mengalami error, sehingga tidak ada yang ditambahkan | Tetap kirim tool_result, dengan is_error: true dan teks error. |
| tool_result masuk ke pesan yang lebih baru | Tool_result harus berada di pesan yang langsung berikutnya — tidak boleh ada turn assistant atau user di antaranya. |
| tool_use_id tidak cocok | Salin id persis dari blok tool_use; jangan membuatnya ulang. |
| Riwayat dipangkas di tengah perjalanan pulang-pergi | Pangkas pada batas perjalanan pulang-pergi tool secara utuh, jangan di antara dua bagiannya. |
Invarian, dinyatakan sekali
Setiap blok tool_use dalam turn assistant memerlukan tepat satu blok tool_result dalam pesan user yang langsung berikutnya, dengan tool_use_id yang sama. Beberapa blok tool_use dalam satu turn memerlukan beberapa blok tool_result dalam satu pesan berikutnya itu. Tidak boleh ada apa pun di antara kedua turn tersebut.
Selalu balas, bahkan ketika tool gagal
Model dapat menangani tool yang gagal dengan baik; model tidak dapat menangani tool yang hilang. Mengembalikan error sebagai tool_result menjaga percakapan tetap valid dan biasanya menghasilkan pemulihan yang masuk akal, bukan 400.
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})Validasikan dua turn terakhir sebelum mengirim
Sekitar selusin baris assertion menangkap ini di lokasi pemanggilan, bukan sebagai 400 dari jaringan — telusuri id tool_use pada turn assistant dan pastikan turn user berikutnya menjawab semuanya.
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}"Pangkas riwayat pada batas perjalanan pulang-pergi
Pemangkasan context window berdasarkan jumlah pesan pada akhirnya akan memotong di antara tool_use dan tool_result. Perlakukan pasangan tersebut sebagai satu unit yang tidak dapat dipisahkan saat menentukan apa yang akan dihapus.
Jika Anda melakukan panggilan melalui Kunavo
Payload ini adalah tanggung jawab Anda, dan Kunavo tidak menutupinya: 400 termasuk dalam daftar yang tidak dapat di-retry, sehingga perjalanan tool yang malformasi gagal sekali alih-alih menghabiskan perjalanan pulang-pergi kedua ke upstream hanya untuk mencapai error yang sama, dan request yang ditolak dicatat dengan biaya nol. Pada /v1/messages Anda berbicara langsung menggunakan protokol Messages, sehingga blok tools, tool_use, dan tool_result diteruskan tanpa diterjemahkan. Di sana 400 dikembalikan dengan tipe invalid_request_error, seperti pada API Anthropic, disertai teks pesan upstream lalu request id milik upstream dan tanpa field request_id; hingga 24 September 2026 tipenya terbaca api_error, jadi gunakan status HTTP dan teks pesan sebagai cabang jika log Anda mencakup periode sebelumnya.
Pertanyaan umum
Bisakah saya menghapus turn tool_use begitu saja alih-alih menjawabnya?
Ya, jika Anda menghapus seluruh turn assistant. Yang tidak valid adalah mempertahankan tool_use dan menghilangkan tool_result-nya.
Apakah endpoint OpenAI-compatible memiliki aturan yang sama?
Pasangan yang sama tetap diperlukan, dengan penamaan berbeda — tool_calls pada pesan assistant, lalu satu pesan role: "tool" untuk setiap pemanggilan yang membawa tool_call_id.
Apakah request yang ditolak dikenai biaya?
Di Kunavo, tidak. Request yang gagal dicatat dengan biaya nol dan tidak pernah mencapai pemanggilan upstream yang ditagihkan.
Panduan terkait
- model_not_found / 404 — penamaan model di Claude, Gemini, dan gateway
- Claude API 429 rate_limit_error — penyebab dan perbaikan yang efektif
Detail semantik error lainnya tersedia di referensi error; mendapatkan kunci hanya perlu satu menit melalui pendaftaran dan panduan autentikasi.