Sua API retorna 400 Bad Request com o corpo {"error": "invalid input"}. Um desenvolvedor humano abre a documentação, verifica o payload, encontra o campo ausente e o corrige em um minuto. Um agente lê as mesmas duas palavras, não tem nada em que agir e faz a única coisa que pode: envia a mesma requisição novamente. E de novo. Então ele desiste e diz ao usuário que a API está quebrada.
As respostas de erro são a parte de uma API na qual os agentes mais dependem e que as equipes projetam por último. Um bom erro informa ao chamador o que deu errado, se tentar novamente poderia ajudar e o que mudar. Um agente pode agir em todas as três. Um erro vago transforma um problema recuperável em uma tarefa falhada.
Este guia é escrito para o lado da API do relacionamento. Nossa postagem sobre recuperação de erros de agente aborda o que o cliente deve fazer com retentativas, backoff e disjuntores. Este aqui aborda o que sua API precisa retornar para que essa lógica do cliente funcione.
Apidog é importante aqui porque as respostas de erro são a parte menos testada da maioria das APIs. Você pode defini-las na especificação, simulá-las e fazer asserções sobre elas no mesmo lugar onde testa o caminho feliz.
As três perguntas que um erro deve responder
Toda resposta de erro que um agente recebe deve permitir que ele responda a três coisas sem adivinhar.
A culpa é minha ou sua? Um 4xx significa que a requisição estava errada e repeti-la sem alterações falhará novamente. Um 5xx significa que algo no servidor deu errado e a mesma requisição pode ter sucesso mais tarde. Agentes que não conseguem diferenciar isso ou tentam novamente para sempre em um erro de validação ou desistem de um problema transitório.
Devo tentar novamente e quando? Alguns erros 4xx são retentáveis e outros não. 429 é retentável após uma espera. 409 pode ser retentável após reler o estado. 422 não é retentável sem alterar o payload. Diga qual, explicitamente.
O que exatamente eu mudo? Este é o campo que a maioria das APIs omite. “Validation failed” é inútil. “O campo customer.postal_code é obrigatório quando country é US” é uma correção que o agente pode aplicar na próxima tentativa.
Inclua essas três informações em cada erro e a maioria das tempestades de retentativas de agentes desaparecerá.
Use um formato de erro estruturado
Não invente um formato. A RFC 9457, Problem Details for HTTP APIs, define um e é bem suportada:
{
"type": "https://api.example.com/errors/validation-failed",
"title": "Validation failed",
"status": 422,
"detail": "The field 'customer.postal_code' is required when 'country' is 'US'.",
"instance": "/v1/orders",
"errors": [
{
"field": "customer.postal_code",
"code": "required_conditional",
"message": "Required when country is US. Provide a 5-digit or 9-digit US postal code.",
"example": "94107"
}
],
"retryable": false,
"next_action": "Add customer.postal_code to the request body and send again."
}
Quatro partes carregam o peso para um agente.
detail é uma frase completa que nomeia o campo real e a regra real. Não uma categoria. A coisa específica que falhou nesta requisição.
O array errors é legível por máquina, uma entrada por problema, com um caminho de campo que um agente pode mapear de volta para o payload que enviou. Retorne cada falha de uma vez. Retorná-los um por um transforma uma única correção em cinco viagens de ida e volta.
retryable é um booleano, não algo a ser inferido de um código de status. Esta é a extensão que mais ajuda os agentes, e custa um campo.
next_action é um texto de instrução simples. Os modelos seguem instruções explícitas em um corpo de resposta de forma mais confiável do que raciocinam a partir de códigos de erro, e uma frase aqui frequentemente transforma uma tarefa falhada em uma concluída.
O guia de design de erros de API do Google chega a conclusões semelhantes de uma direção diferente, notavelmente que os detalhes do erro pertencem a uma lista estruturada em vez de em prosa.
Diga quando voltar
Para qualquer coisa transitória, diga quando. Um agente que sabe esperar 30 segundos espera 30 segundos. Um agente que não sabe escolherá algo, e esse algo geralmente é muito curto.
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/problem+json
{
"type": "https://api.example.com/errors/rate-limited",
"title": "Rate limit exceeded",
"status": 429,
"detail": "You have used 1000 of 1000 requests in the current minute window.",
"retryable": true,
"retry_after_seconds": 30,
"next_action": "Wait 30 seconds before sending this request again. Do not retry sooner."
}
O cabeçalho Retry-After aceita um atraso em segundos ou uma data HTTP; segundos é o mais fácil para um cliente agir. Envie-o como um cabeçalho para clientes padrão e repita-o no corpo para o modelo. A duplicação é barata e ambos os consumidores obtêm o que melhor entendem. Os detalhes do limite de taxa são abordados em nosso guia de limite de taxa excedido e em como implementar limite de taxa de API se você estiver no lado do servidor.
O mesmo padrão se aplica a 503 durante a manutenção e a 409 em um recurso bloqueado. Qualquer erro em que a espera seja a resposta correta deve conter um número.
Nunca vaze informações internas, nunca retorne nada
Dois modos de falha estão em extremos opostos, e ambos prejudicam os agentes.
O primeiro é o stack trace. Retornar texto de exceção interna expõe versões de framework, caminhos de arquivo e, às vezes, fragmentos de consulta. É um problema de segurança antes de ser um problema para o agente, e as preocupações em nossa postagem sobre testar APIs contra entrada não confiável se aplicam diretamente. Também inunda a janela de contexto com texto que o modelo não pode usar.
O segundo é o erro vazio: um 500 sem corpo, ou {"error": true}. O agente não aprende nada, e suas únicas opções são tentar novamente ou desistir.
O caminho do meio é um erro público estável com um ID de correlação:
{
"type": "https://api.example.com/errors/internal",
"title": "Internal error",
"status": 500,
"detail": "The order could not be created due to an internal error. No order was created.",
"retryable": true,
"retry_after_seconds": 5,
"request_id": "req_01J8ZK3M2Q",
"next_action": "Retry once after 5 seconds. If it fails again, stop and report request_id req_01J8ZK3M2Q."
}
A frase “Nenhum pedido foi criado” é a parte mais valiosa. Agentes que enfrentam uma escrita ambígua precisam decidir se tentar novamente arrisca uma duplicação, e a maioria decide mal. Diga a eles em que estado você está. Onde você não pode prometer isso, torne a operação idempotente e diga isso, que é o padrão em nossa postagem sobre chaves de idempotência para agentes de IA.
O request_id leva você de volta aos seus logs quando um humano finalmente lê a transcrição. Combine-o com as práticas em nosso guia de observabilidade de API para que o ID realmente se resolva em algo.
Erros pertencem à especificação
Se um formato de erro não estiver em seu documento OpenAPI, ele não existe para clientes gerados, mocks e ferramentas de agente. A maioria das especificações descreve um 200 em detalhes e depois acena para todo o resto.
responses:
'201':
description: Pedido criado
content:
application/json:
schema: { $ref: '#/components/schemas/Order' }
'422':
description: >
Falha na validação. Não retentável sem alterar o corpo da requisição.
O array de erros nomeia cada campo inválido.
content:
application/problem+json:
schema: { $ref: '#/components/schemas/Problem' }
'429':
description: >
Limite de taxa excedido. Retentável. Aguarde retry_after_seconds antes de enviar novamente.
content:
application/problem+json:
schema: { $ref: '#/components/schemas/Problem' }
Essas descrições não são decoração. Quando você gera ferramentas de agente a partir da especificação, como em nosso guia para transformar uma especificação OpenAPI em ferramentas de agente, esse texto se torna o que o modelo lê sobre o caso de falha. Uma descrição que diz “retentável, espere primeiro” produz um comportamento melhor do que uma que diz “Too Many Requests”.
Teste os erros, não apenas os sucessos
Os caminhos de erro são onde a cobertura de testes colapsa, porque acioná-los exige esforço. A simulação remove esse esforço.

Defina cada resposta de erro em seu projeto de API e, em seguida, simule-as para que o agente possa lidar com cada caso sob demanda. No Apidog, você pode adicionar as respostas de falha à definição do endpoint e alternar um mock entre elas, o que lhe dá uma maneira repetível de executar o agente contra um 422, um 429 e um 500 sem quebrar nada real. Nossa postagem sobre executar agentes contra mocks em vez de produção abrange o hábito mais amplo.
Cinco casos para construir:
- Falha de validação com vários campos inválidos de uma vez. Garanta que cada problema retorne em uma única resposta e que a próxima tentativa do agente corrija todos eles, em vez de apenas um.
- Limite de taxa com uma espera. Garanta que o agente espere pelo menos
retry_after_secondsem vez de martelar. - Erro de servidor em uma escrita. Garanta que o agente não crie duplicatas silenciosamente ao tentar novamente.
- Falha de autenticação. Garanta que o agente pare em vez de tentar novamente, já que nenhuma quantidade de espera corrige um token inválido. Nossa postagem sobre chaves de API de privilégio mínimo para agentes de IA aborda o lado das credenciais.
- Corpo de erro malformado. Retorne algo que não seja um JSON válido e confirme que o agente degrada graciosamente. Proxies upstream farão isso com você eventualmente.
Salve o conjunto como cenários para que sejam executados em CI. O tratamento de erros regride silenciosamente, geralmente quando alguém refatora um serializador, e o conjunto de testes de caminho feliz não notará.
O valor de erros melhores
O valor aparece em três lugares, e é fácil de medir uma vez que você olha.
Menos retentativas desperdiçadas. Um agente que encontra {"error": "invalid input"} tipicamente tenta novamente o payload idêntico duas ou três vezes antes de desistir. Cada tentativa custa um turno do modelo e a conversa completa como contexto. Uma resposta nomeando o campo ausente geralmente produz uma tentativa corrigida. Essa é a diferença entre quatro chamadas e duas em um erro de validação rotineiro.
Menos escalonamentos. Agentes que não conseguem se recuperar entregam a tarefa a um humano. Cada transferência evitável é o resultado caro que o agente deveria ter impedido. Erros que nomeiam uma correção mantêm a execução dentro da automação.
Depuração mais curta. Quando algo realmente precisa de uma pessoa, request_id mais um detail preciso transforma uma busca em logs em uma única consulta. Este é o mesmo argumento que nosso guia de observabilidade de API faz sobre correlação, aplicado ao momento em que uma execução falha.
Há um quarto benefício que é fácil de perder: as mesmas melhorias ajudam os desenvolvedores humanos. Ninguém nunca reclamou que uma mensagem de erro era muito específica sobre qual campo estava errado.
Projete também para o escalonamento
Alguns erros são genuinamente irrecuperáveis pelo agente. Um escopo ausente, uma conta encerrada, uma regra que precisa de uma decisão humana. Para esses, o trabalho do erro é entregar a tarefa de forma limpa: dizer o que aconteceu, dizer o que uma pessoa precisa fazer e carregar o ID de correlação que torna a entrega barata.
Essa resposta precisa chegar a um lugar onde um humano possa lê-la. Se o agente é um tempo de execução de codificação trabalhando em tarefas atribuídas, a plataforma circundante é geralmente onde ela chega. O Sharkly mantém o resultado do agente e o rastreamento da execução na Tarefa e encaminha itens que precisam de uma resposta ou revisão para uma Caixa de Entrada, de modo que uma execução bloqueada seja visível como trabalho, em vez de como uma linha em um log. O texto do seu erro é o que torna essa entrega útil, porque uma mensagem que diz “entrada inválida” não dá ao revisor mais do que deu ao agente.

Não force o agente a analisar prosa
Um último anti-padrão, comum em APIs que cresceram organicamente. O código de status está correto, o corpo é uma frase, e cada falha distinta recebe uma redação diferente:
{ "message": "Sorry, that didn't work. Please check your details and try again." }
Um agente só pode responder a isso adivinhando. Pior, as equipes frequentemente o combinam com um status 200, então a biblioteca cliente nem sequer vê uma falha.
Duas regras resolvem isso. Dê a cada falha distinta um código estável legível por máquina, para que o agente possa ramificar em insufficient_funds em vez de na frase “não é suficiente”. E nunca retorne uma falha com um código de status de sucesso, independentemente do argumento de conveniência do lado do cliente. Um 200 com um erro dentro é invisível para cada política de retentativa, cada dashboard e cada alerta que você possui.
Uma lista de verificação para erros legíveis por agentes
- Cada erro usa um formato estruturado consistente em toda a API.
detailnomeia o campo ou condição específica, nunca uma categoria.- Erros de validação retornam todos os problemas de uma vez, com caminhos de campo.
- Um booleano
retryableaparece em cada erro. - Erros retentáveis carregam uma espera em segundos, no cabeçalho e no corpo.
- Falhas de escrita informam se algo foi criado ou alterado.
- Cada erro carrega um ID de correlação que se resolve em seus logs.
- Sem stack traces, sem strings de framework, sem SQL.
- As respostas de erro são documentadas na especificação com descrições legíveis por agentes.
- Mocks existem para cada erro, e testes salvos os executam em CI.
Erros são uma interface. Projete-os para o chamador que você realmente tem, que é cada vez mais um modelo que fará exatamente o que o corpo da sua resposta lhe disser para fazer. Baixe o Apidog para definir os formatos de erro e simulá-los antes que o agente os encontre de verdade.
Perguntas Frequentes
Devo usar a RFC 9457 ou meu próprio formato de erro? Use a RFC 9457, a menos que você já tenha um formato consistente em produção. A consistência supera a padronização: mudar metade dos seus endpoints para um novo formato é pior do que manter um único formato em todos os lugares. Adicione as extensões retryable e next_action ao que você usa.
É seguro colocar texto em next_action em uma resposta de API? Sim, quando seu serviço o gera a partir de um conjunto fixo de templates. Nunca ecoe conteúdo fornecido pelo usuário nesse campo, pois um agente o lê como instrução e isso é um caminho para injeção de prompt. Nossa postagem sobre testar APIs contra entrada não confiável aborda o risco.
Erros de validação devem ser 400 ou 422? Use 400 quando a requisição estiver malformada, como um JSON quebrado, e 422 quando a requisição for analisada, mas falhar nas regras de negócio. Os agentes se beneficiam da distinção porque as correções são diferentes. Se você já usa um para ambos, documente-o em vez de alterá-lo.
Quanto detalhe é demais? Pare no ponto em que o chamador tem informações suficientes para agir. Nome do campo, regra e um valor de exemplo geralmente são suficientes. Identificadores internos, texto de consulta e stack frames ultrapassam o limite.
As mensagens de erro contam para a janela de contexto? Sim, e um erro verboso repetido em várias retentativas se acumula rapidamente. Mantenha-os abaixo de algumas centenas de tokens. Nossa postagem sobre reduzir respostas de API para agentes se aplica tanto a falhas quanto a sucessos.
Como impeço um agente de tentar novamente um erro não retentável? Defina retryable: false, declare isso em next_action, e imponha-o no wrapper da ferramenta para que o julgamento do modelo não seja a única salvaguarda. Usar cinto e suspensórios é o correto aqui.
