Erros e códigos de estado

Max PrilutskiyCEO e cofundador8 min de leitura

A chamada funciona no ambiente de desenvolvimento. Agora você está escrevendo a parte que vai rodar em produção – o bloco catch. Por si só, um erro HTTP de uma API de terceiros não esclarece muita coisa: um código de estado em vermelho e nenhuma resposta óbvia à única pergunta que importa às 3 da manhã – o problema está na minha requisição, na minha chave, no meu plano ou nos servidores deles? E quais desses erros pedem uma nova tentativa e quais devo mostrar ao usuário?

A Lingo.dev responde a isso com uma estrutura consistente. Todo erro dos endpoints destas páginas, síncronos ou assíncronos, retorna o mesmo objeto JSON, com um código de estado de uma tabela fixa. Já a API da plataforma em /v2 usa o formato padrão de detalhes de problemas; veja Erros da API da plataforma. O código de estado não é um rótulo; é uma instrução: ele indica se você deve corrigir a requisição, trocar a chave, adicionar créditos à conta, dar um tempo ou tentar novamente. Leia o código e saiba o próximo passo. Um único tratamento de erros, baseado no código de estado, cobre toda a API.

Nesta página

Formato dos erros#

Toda resposta fora da faixa 2xx tem o mesmo corpo: um objeto JSON com um único campo message que descreve o que deu errado.

json
{
  "message": "Invalid API key"
}

Esse é o contrato completo. Não há estrutura externa para descompactar nem formato de erro específico de cada endpoint para tratar à parte. Um 400 de /process/localize e um 404 de uma consulta de tarefa retornam o mesmo formato – só mudam o código de estado e o texto de message.

Baseie o tratamento no código de estado, não no texto da mensagem

O código de estado HTTP é o sinal estável – use-o para decidir como tratar cada erro. O texto de message foi escrito para quem está lendo um registro; trate-o como uma descrição, não como um código de erro legível por máquina, e não dependa de uma correspondência exata com esse texto.

Códigos de estado#

Sete códigos de estado cobrem todas as respostas. Aqui, eles estão agrupados por quem resolve o problema – porque esse agrupamento também define sua política de novas tentativas.

Você enviou algo que não pode ser atendido na requisição (corrija a requisição, não tente novamente às cegas):

EstadoSignificado
400 Bad RequestA validação da requisição falhou – um campo ausente, um idioma inválido, um callbackUrl com HTTP (não HTTPS) ou um corpo de requisição malformado.
401 UnauthorizedO cabeçalho X-API-Key está ausente ou é inválido. Veja Autenticação.
403 ForbiddenA chave é válida, mas não tem acesso ao recurso solicitado.
404 Not FoundO recurso – um engine, uma tarefa, um grupo de tarefas – não existe.

Sua organização atingiu um limite da conta (resolva no faturamento):

EstadoSignificado
402 Payment RequiredA organização atingiu seu limite de crédito.
429 Too Many RequestsA organização atingiu sua cota diária de tokens. Mude para um plano superior para aumentar o limite.

Algo falhou do nosso lado (falha transitória – tente novamente):

EstadoSignificado
500 Internal Server ErrorUma falha inesperada – um erro de banco de dados ou uma chamada de tradução que falhou em todos os modelos configurados no engine.

Um 401 e um 403 parecem semelhantes, mas não representam o mesmo problema: 401 significa que não conseguimos identificar quem fez a chamada; 403 significa que identificamos a chave, mas ela não tem permissão de acesso. Para resolver um 401, é preciso agir sobre a própria chave (troque-a ou verifique-a); para resolver um 403, é preciso ajustar o acesso da chave.

Quais erros permitem novas tentativas#

A primeira pergunta de um integrador cético sobre qualquer tabela de erros costuma ser justamente a que fica sem resposta: em quais desses casos devo tentar novamente? O agrupamento acima responde a isso.

  • 4xx – não tente novamente às cegas. Um 400, 401, 403 ou 404 descreve uma condição na sua requisição. Repetir a mesma requisição gera o mesmo erro. Corrija os dados de entrada, a chave ou o ID do recurso e envie novamente.
  • 402 e 429 – dê um tempo e resolva o limite. Esses erros não são transitórios no nível da requisição; a próxima requisição esbarra no mesmo limite até que ele mude. Pare de repetir chamadas sem pausa, informe o limite e resolva a situação (adicione créditos ou mude para um plano superior).
  • 500 – tente novamente com intervalos de espera. Essa é a única categoria realmente transitória. Um 500 pode significar que todos os modelos configurados excederam o tempo limite naquela chamada; uma nova tentativa pode chegar a um modelo que esteja funcionando. Aumente os intervalos de espera exponencialmente e defina um número máximo de tentativas.

A API assíncrona informa os resultados de outra forma

Essa política de novas tentativas vale para chamadas síncronas que você faz diretamente. A API de localização assíncrona não fornece um código de estado para o resultado do trabalho: um POST retorna 202 assim que a requisição é aceita, e cada idioma de destino é processado como uma tarefa independente por meio de fluxos de trabalho persistentes em segundo plano. Você consulta periodicamente a tarefa ou recebe um webhook com o resultado, em vez de capturar um código de estado na chamada original. Veja onde ficam os erros de tarefas assíncronas.

402 e 429: dois limites diferentes#

Os dois códigos no nível da conta parecem dizer a mesma coisa – ambos soam como "você esgotou o limite" – e confundi-los leva o desenvolvedor à solução errada. São limites diferentes, com soluções diferentes:

  • 402 Payment Required – a organização atingiu seu limite de crédito. Esse é um limite de faturamento. A próxima chamada continuará falhando até que a situação de faturamento da organização mude.
  • 429 Too Many Requests – a organização atingiu sua cota diária de tokens. Esse é um teto de consumo que se renova e pode ser aumentado com a mudança para um plano superior.

O motivo para separar esses casos no tratamento de erros: um 402 exige que uma pessoa tome uma providência no faturamento; um 429 indica uma cota que você pode esperar renovar ou aumentar mudando para um plano superior. Exibir uma mensagem genérica de "problema de pagamento" nos dois casos esconde qual ação o operador realmente precisa tomar.

O corpo de um 402 é igual ao de qualquer outro erro – é o código de estado que indica que se trata de um limite de crédito:

json
{
  "message": "Organization has reached its credit limit"
}

Onde ficam os erros de tarefas assíncronas#

Vale fazer uma distinção aqui, porque é nesse ponto que o tratamento por código de estado deixa de ser a ferramenta certa.

Os códigos de estado desta página são do nível de transporte: eles descrevem se a API aceitou e conseguiu atender à sua requisição HTTP. Um 202 da API assíncrona significa que sua requisição foi aceita – não que a tradução foi concluída com sucesso. Uma tarefa assíncrona pode ser aceita sem problemas e ainda assim falhar depois, quando um modelo excede o tempo limite durante a execução. Essa falha não aparece como um código de estado HTTP na chamada original; ela fica registrada na própria tarefa.

Assim, as falhas assíncronas aparecem em três lugares, nenhum deles nesta tabela:

  • Estado de cada tarefa. Quando o processamento de um idioma falha, a tarefa apresenta status: "failed" e um errorMessage. Veja estados das tarefas.
  • Estado do grupo. Quando alguns idiomas são processados com sucesso e outros falham, o grupo informa partial – as traduções dos idiomas concluídos com sucesso continuam sendo entregues. Veja como acompanhar um grupo de tarefas.
  • Entrega por webhook. Uma falha é entregue como um evento translation.failed com um campo error. Veja entrega por webhook.

Há mais uma distinção que costuma causar confusão: a falha de uma etapa não crítica do fluxo de processamento não faz a tarefa falhar. A tarefa é concluída com completed_with_warnings e avisos por etapa, em vez de um erro. Isso é uma questão de observabilidade do fluxo de processamento, não de código de erro – veja como observar as execuções do fluxo de processamento.

Erros da API da plataforma#

A API da plataforma em https://api.lingo.dev/v2 responde aos erros de outra forma. Ela retorna detalhes de problemas conforme a RFC 9457, enviados como application/problem+json:

json
{
  "type": "https://lingo.dev/docs/api/errors/glossary-in-use",
  "status": 409,
  "title": "Glossary in use",
  "detail": "Glossary gls_1 cannot be deleted while 2 engines apply it. Detach it from those engines first.",
  "id": "gls_1",
  "engineCount": 2
}
  • type identifica o problema. É um link para a página específica desse problema, que lista seus campos e os endpoints que o retornam. Use type para decidir como tratar o erro.
  • status repete o código de estado HTTP.
  • title é um resumo curto, igual para todas as ocorrências de um mesmo tipo.
  • detail informa o que aconteceu desta vez e o que fazer em seguida. É escrito para uma pessoa, então exiba-o em vez de interpretá-lo por código.
  • Outros membros contêm os dados desta ocorrência, como id e engineCount acima. detail é construído apenas a partir deles, então um cliente pode gerar seu próprio texto, no seu próprio idioma, com base em type e nesses campos.

Uma requisição que falha na validação retorna 400 e lista cada valor inválido em errors, como pares { "path", "message" }. Todo endpoint também pode retornar service-unavailable (503), que permite novas tentativas com segurança usando intervalos de espera, e internal-server-error (500).

Cada código de estado segue uma regra:

EstadoSignificado
401Quem fez a chamada não foi identificado.
402Um limite do plano ou dos direitos de acesso, que pode ser ampliado mudando para um plano superior ou adicionando créditos.
403Quem fez a chamada foi identificado, mas não pode realizar esta ação.
404O recurso não existe ou quem fez a chamada não consegue vê-lo.
409A requisição entra em conflito com o estado atual, por exemplo, um recurso que ainda está em uso.
422A requisição está bem formada, mas uma regra a rejeita.
429Um limite que se renova com o tempo.
502Um provedor externo do qual o serviço depende falhou.
503Uma falha temporária do nosso lado. Tente novamente com intervalos de espera.

Próximos passos#

Um tratamento de erros bem estruturado começa pelos dois pontos que você encontrará primeiro durante a integração – a autenticação e os locais em que o processamento assíncrono informa seu próprio resultado.