Voltar aos guias
Solução de problemas·28 de agosto de 2026·6 min de leitura

Claude API 400 “tool_use ids foram encontrados sem blocos tool_result” — a regra de ordenação

Este é um erro de ordenação de mensagens, não um erro de ferramentas. O Claude exige que todo bloco tool_use em um turno do assistant seja respondido por um bloco tool_result no turno user imediatamente seguinte — com os mesmos IDs e sem nada entre eles. Seu loop deixou um de fora, geralmente porque a ferramenta lançou uma exceção.

Última revisão em .

Este é um erro de ordenação de mensagens, não um erro de ferramentas. O Claude exige que todo bloco tool_use em um turno do assistant seja respondido por um bloco tool_result no turno user imediatamente seguinte — com os mesmos IDs e sem nada entre eles. Seu loop deixou um de fora, geralmente porque a ferramenta lançou uma exceção.

O erro

response (HTTP 400)
{
  "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."
  }
}

Causas e soluções em resumo

CausaSolução
A ferramenta lançou uma exceção, então nada foi acrescentadoAinda envie um tool_result, com is_error: true e o texto do erro.
O tool_result foi colocado em uma mensagem posteriorEle precisa estar na mensagem imediatamente seguinte — nenhum turno assistant ou user pode ficar entre os dois.
tool_use_id não correspondeReproduza o ID exato do bloco tool_use; não o gere novamente.
O histórico foi reduzido no meio da ida e voltaReduza o histórico em ciclos completos de ida e volta, nunca entre as duas metades de um ciclo.

A invariável, em uma frase

Todo bloco tool_use em um turno assistant precisa de exatamente um bloco tool_result na mensagem user imediatamente seguinte, contendo o mesmo tool_use_id. Vários blocos tool_use em um turno precisam de vários blocos tool_result nessa única mensagem seguinte. Nada pode ficar entre os dois turnos.

Sempre responda, mesmo quando a ferramenta falhar

O modelo lida perfeitamente bem com uma ferramenta que falhou; o que ele não consegue lidar é com uma resposta ausente. Retornar o erro como tool_result mantém a conversa válida e normalmente produz uma recuperação sensata, em vez de um 400.

tool_loop.py
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})

Valide os dois últimos turnos antes do envio

Uma dúzia de linhas de asserção detecta isso no ponto de chamada, em vez de como um 400 vindo da rede — percorra os IDs tool_use do turno assistant e confirme que o turno user seguinte responde a todos eles.

validate.py
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}"

Reduza o histórico nos limites dos ciclos de ida e volta

A redução da janela de contexto por contagem de mensagens acabará cortando entre um tool_use e seu tool_result. Trate o par como uma unidade indivisível ao decidir o que descartar.

Se você estiver chamando pela Kunavo

O problema está no seu payload, e a Kunavo não o encobre: 400 está na lista de erros que não devem ser repetidos, portanto um ciclo de ferramenta malformado falha uma vez, em vez de gastar uma segunda ida e volta ao upstream para chegar ao mesmo erro, e a solicitação rejeitada é registrada com custo zero. Em /v1/messages, você fala diretamente o protocolo Messages, então os blocos tools, tool_use e tool_result são encaminhados sem tradução. Nesse caso, o 400 retorna tipado como invalid_request_error, como na API da Anthropic, com o texto da mensagem do upstream seguido pelo próprio request id do upstream e sem um campo request_id; até 24 de setembro de 2026, o tipo era api_error, portanto use o status HTTP e o texto da mensagem para ramificar se seus logs forem mais antigos.

Perguntas frequentes

Posso simplesmente remover o turno tool_use em vez de respondê-lo?

Sim, se você remover o turno assistant inteiro. O que é inválido é manter o tool_use e omitir seu tool_result.

O endpoint compatível com a OpenAI tem a mesma regra?

O mesmo pareamento é obrigatório, com nomes diferentes — tool_calls na mensagem assistant e depois uma mensagem role: "tool" para cada chamada, contendo tool_call_id.

Uma solicitação rejeitada gera cobrança?

Na Kunavo, não. Solicitações que falham são registradas com custo zero e nunca chegam a uma chamada upstream faturada.

Guias relacionados

Mais detalhes sobre o significado dos erros estão em referência de erros; obter uma chave leva um minuto por meio de cadastro e da guia de autenticação.