# Erros e novas tentativas

Códigos de erro, status HTTP, o que fazer em cada caso e quando repetir uma requisição.

Versão HTML: https://kind-coast-062131010.2.azurestaticapps.net/guias/erros/

Erros têm sempre o mesmo formato: um objeto com o código em `error`.

```json
{ "error": "INVALID_REQUEST" }
```

Não há mensagem detalhada. Erros de validação não dizem qual campo falhou: confira o corpo contra a [referência](https://kind-coast-062131010.2.azurestaticapps.net/referencia/resolve-classification.md) e o guia [Dados da consulta](https://kind-coast-062131010.2.azurestaticapps.net/guias/dados-da-consulta.md).

## Códigos

| HTTP | `error` | Quando | O que fazer |
| --- | --- | --- | --- |
| 400 | `INVALID_REQUEST` | JSON malformado, campo obrigatório ausente, GTIN fora do formato ou com dígito verificador errado, CFOP sem 4 dígitos, setor desconhecido, data inválida, campo não previsto; no lote, envelope inválido (vazio, mais de 100 itens, campo além de `items`, item sem `gtin` ou `cfop` em texto). | Corrija a requisição. Não repita igual. Não consome franquia. |
| 401 | `INVALID_API_KEY` | Cabeçalho ausente ou fora do formato `Bearer gtx_…`; chave inexistente, revogada ou substituída; cliente desativado. | Confira o cabeçalho e, no portal da revenda, se a chave está ativa. Persistindo, crie uma nova. Não consome franquia. |
| 404 | `NOT_FOUND` | Rota inexistente. | Confira o método e o caminho. |
| 429 | `QUOTA_EXCEEDED` | Franquia mensal esgotada. Só na consulta unitária; no lote, aparece por item. | Espere a virada do mês ou fale com a equipe GetTax. Não repita antes disso. |
| 500 | `INTERNAL_ERROR` | Erro inesperado. | Repita com backoff. Persistindo, reporte. |
| 503 | `SERVICE_UNAVAILABLE` | Banco ou serviço temporariamente indisponível. | Repita com backoff. Se a falha ocorreu depois da admissão, a unidade já foi consumida. |

O 404 de rota inexistente não tem relação com `status: "NOT_FOUND"` no corpo de uma resposta 200, que diz que o GTIN não existe na GS1.

> [!TIP]
> Sempre envie `Content-Type: application/json` nas requisições com corpo. Uma requisição com outro tipo de conteúdo não é tratada como erro de validação e pode voltar como `500 INTERNAL_ERROR`.

## Quando repetir

Repita só o que pode dar certo sem mudar nada:

| Resposta | Repetir? |
| --- | --- |
| `500`, `503` | Sim, com backoff exponencial. |
| `429` | Não, até a virada do mês. |
| `400`, `401`, `404` | Não. Corrija a requisição ou a chave. |
| `200` com `PENDING` | Mais tarde, respeitando `retryAfterSeconds`. Não em loop. |
| `200` com `IN_REVIEW` | Mais tarde: depende da curadoria fiscal, não de tentativa. |

Um backoff razoável: espere 1 s, depois 2 s, 4 s, 8 s, com uma variação aleatória em cada espera, e desista depois de 4 ou 5 tentativas. Cada nova tentativa de uma consulta que já tinha sido admitida consome outra unidade da franquia.

## Erros no lote

No lote, problemas de um item não derrubam os outros. A resposta é HTTP 200, e cada item recusado volta com `outcome: "REJECTED"` e o próprio `error`:

| `error` do item | Significa | Consome? | O que fazer |
| --- | --- | --- | --- |
| `INVALID_REQUEST` | O item é inválido. Afeta só ele. | Não | Corrija o item. |
| `QUOTA_EXCEEDED` | A franquia acabou neste item. Este e todos os seguintes voltam assim. | Não | Reenvie depois da virada do mês. |
| `INVALID_API_KEY` | A chave deixou de valer durante o lote. Este e todos os seguintes voltam assim. | Não | Confira a chave e reenvie. |
| `SERVICE_UNAVAILABLE` | Falha temporária. Na admissão, afeta este item e os seguintes; ao resolver um item já admitido, afeta só ele. | Só se o item já tinha sido admitido | Reenvie com backoff. |

Reenvie só os itens recusados. Os `RESOLVED` já foram cobrados.

```json
{
  "results": [
    { "outcome": "REJECTED", "index": 0, "error": "INVALID_REQUEST" },
    { "outcome": "REJECTED", "index": 1, "error": "QUOTA_EXCEEDED" },
    { "outcome": "REJECTED", "index": 2, "error": "QUOTA_EXCEEDED" }
  ]
}
```

## Ao reportar um problema

Envie à equipe GetTax: horário da chamada, rota, corpo enviado (sem a chave), status HTTP recebido e o `decisionVersion` da resposta, quando houver.
