Erro de API divide-se em dois grupos, e tratar os dois do mesmo jeito é o erro de integração mais comum que existe. Um grupo é culpa do seu código, e repetir não adianta nada. O outro é transitório, e repetir é exatamente o certo — com backoff.
A referência oficial do Jev documenta quatro códigos, e os SDKs mapeiam alguns outros.
A tabela dos quatro
| Código | Significado oficial | Repetir? |
|---|---|---|
401 Unauthorized | chave ausente ou inválida; confira o cabeçalho Authorization | não |
422 Unprocessable Entity | o corpo reprovou na validação; a resposta detalha o campo | não, sem mudar o corpo |
429 Too Many Requests | limite de taxa excedido | sim, com backoff |
529 Overloaded | a TypeSafe está temporariamente sobrecarregada | sim, com backoff |
Guarde a divisão: 401 e 422 você conserta; 429 e 529 você espera.
401: a chave
Quase sempre é uma destas três coisas: a variável de ambiente não chegou ao
processo, a chave foi copiada com espaço no fim, ou o cabeçalho está montado
sem o prefixo Bearer . A forma correta é
Authorization: Bearer <chave>.
Um detalhe do SDK que evita confusão: a documentação de configuração diz que valores de ambiente vazios ou só com espaço em branco são ignorados. Ou seja, uma variável definida como string vazia se comporta como variável ausente, e o erro é o mesmo.
Nunca repita um 401 em laço. A chave não vai melhorar sozinha.
422: o corpo
Este é o código que mais aparece no primeiro dia e o que mais gente trata errado. Ele significa que o servidor validou a requisição e recusou, e a resposta diz qual campo está errado. Leia o corpo do erro antes de qualquer outra coisa.
As causas mais comuns têm a ver com a forma das perguntas:
criteriade Score que não é uma lista ordenada, ou tem menos de dois níveis;criteriade Choice enviado como lista em vez de mapa de opção para descrição;typeausente ou escrito diferente denoul,choiceouscore;questionsvazio;modelcom um nome que a sua conta não pode enviar.
No SDK de JavaScript, parte dessas validações acontece antes da rede: a
documentação do systemOne diz que ele lança quando as perguntas estão vazias
ou quando o criteria de Score não é uma lista de pelo menos duas entradas. É
melhor assim — falha local é mais rápida e mais barata que 422.
O que não fazer: colocar o 422 na mesma política de retentativa do 429. Um corpo inválido continua inválido na décima tentativa, e você transformou um erro de digitação em uma rajada de requisições.
429: o limite de taxa
Significa que você passou de um dos limites publicados. Não é um erro do corpo, é um pedido para desacelerar.
A referência recomenda repetir com backoff exponencial em vez de repetir
imediatamente, e diz que os SDKs oficiais fazem isso sozinhos com a política
padrão. No SDK Python, a exceção TypeSafeRateLimitError expõe
retry_after_ms, com o tempo que o servidor pediu — quando o servidor manda um
número, obedeça a ele em vez do seu próprio backoff.
Se o 429 é constante e não esporádico, o problema não é de retentativa: é de dimensionamento. Veja limites de taxa.
529: a sobrecarga
Aqui o erro é do outro lado, e a resposta certa é a mesma do 429: esperar e tentar de novo depois de um intervalo curto. A diferença prática é que 529 não tem relação com o seu volume, então aumentar a espera do seu lado não é punição — é cooperação.
Vale desenhar o sistema supondo que isso acontece. Em um fluxo assíncrono, uma fila com repetição resolve. Em um fluxo síncrono na frente de um usuário, decida antes qual é o comportamento aceitável: esperar, responder com uma decisão padrão ou mandar o caso para uma fila.
As exceções dos SDKs
O SDK Python organiza tudo em uma hierarquia:
| Exceção | Quando |
|---|---|
TypeSafeError | base de todas as falhas do SDK |
TypeSafeAPIError | resposta HTTP sem sucesso; traz status, body, headers, endereço da requisição e request_id |
TypeSafeBadRequestError | 400 |
TypeSafeAuthenticationError | 401 |
TypeSafePermissionDeniedError | 403 |
TypeSafeNotFoundError | 404 |
TypeSafeUnprocessableEntityError | 422 |
TypeSafeRateLimitError | 429, com retry_after_ms |
TypeSafeInternalServerError | 5xx |
TypeSafeAPIConnectionError | falha sem resposta HTTP |
TypeSafeAPITimeoutError | tempo limite estourado |
TypeSafeAPIResponseValidationError | resposta bem-sucedida com corpo fora do formato; traz field_path |
Duas dessas merecem atenção especial.
request_id vem do cabeçalho x-typesafe-request-id. Registre sempre. É o
que permite falar com o suporte sobre uma requisição específica em vez de sobre
uma impressão.
TypeSafeAPIResponseValidationError é a mais fácil de ignorar e a mais
confusa quando acontece: o HTTP deu certo, e o corpo não trouxe o que era
esperado. O field_path aponta o caminho, no formato answers.tone.confidence
— o que, aliás, é um lembrete útil de que Noul não devolve confidence, como
explicado em
probabilidade não é confiança.
Um esqueleto de tratamento
from typesafe_sdk import (
TypeSafeAuthenticationError,
TypeSafeRateLimitError,
TypeSafeUnprocessableEntityError,
TypeSafeAPIError,
)
try:
resposta = client.system_one(state=estado, questions=perguntas)
except TypeSafeUnprocessableEntityError as erro:
# Erro nosso: registrar o campo e NÃO repetir.
log.error("corpo inválido", extra={"body": erro.body, "req": erro.request_id})
raise
except TypeSafeAuthenticationError:
# Erro de configuração: alertar, não repetir.
raise
except TypeSafeRateLimitError as erro:
# O SDK já repetiu; se chegou aqui, a fila precisa desacelerar.
fila.reduzir_paralelismo(espera_ms=erro.retry_after_ms)
raise
except TypeSafeAPIError as erro:
log.error("falha da API", extra={"status": erro.status, "req": erro.request_id})
raise
Um aviso que importa no Brasil
A documentação do SDK Python avisa que, no nível de log debug, cabeçalhos
secretos são mascarados mas os corpos de requisição e resposta não são. Se
o seu state carrega dado pessoal, deixar debug ligado em produção coloca
esse dado no log. Vale checar antes de subir.
Para os padrões de repetição, veja retentativas e tempo limite. Para a chamada crua, a API HTTP sem SDK. O índice está em como usar.
Perguntas frequentes
Quais códigos de erro a API do Jev retorna?
A referência oficial documenta quatro: 401 para chave ausente ou inválida, 422 para corpo reprovado na validação, 429 para limite de taxa excedido e 529 quando a TypeSafe está sobrecarregada. Os SDKs mapeiam também 400, 403, 404 e a faixa 5xx.
Posso repetir um 422?
Não sem mudar o corpo. 422 significa que a requisição foi rejeitada na validação e a resposta indica qual campo está errado. Repetir o mesmo corpo produz exatamente o mesmo 422, consome cota e atrasa o conserto.
Como trato 429 e 529?
Repetindo com backoff exponencial, nunca imediatamente. A referência recomenda isso explicitamente, e os SDKs oficiais já fazem por padrão. Em 429 o SDK Python expõe retry_after_ms, com o tempo que o servidor pediu.
Qual exceção o SDK Python lança em cada caso?
TypeSafeError é a base. TypeSafeAPIError cobre respostas HTTP e traz status, body, headers, endpoint e request_id. Dela descendem TypeSafeBadRequestError (400), TypeSafeAuthenticationError (401), TypeSafePermissionDeniedError (403), TypeSafeNotFoundError (404), TypeSafeUnprocessableEntityError (422), TypeSafeRateLimitError (429) e TypeSafeInternalServerError (5xx).
O que é o request_id e para que serve?
É o cabeçalho x-typesafe-request-id, exposto pela exceção do SDK. Registre-o junto com o erro: é o identificador que permite conversar com o suporte sobre uma requisição específica.
E quando a resposta chega com o formato errado?
O SDK Python lança TypeSafeAPIResponseValidationError, com um field_path apontando o campo, no formato answers.tone.confidence. É uma resposta HTTP bem-sucedida cujo corpo não trouxe o que era esperado.