Pular para o conteúdo principal

Tratamento de erros

Todas as exceções do SDK derivam de nfeio.NfeError e podem ser importadas direto de nfeio (ou de nfeio.errors). Nenhuma delas carrega a chave de API, em mensagem, atributo ou repr.

Hierarquia​

NfeError
├── ConfigurationError chave ausente, opção inválida, TLS inseguro (também ValueError)
├── InvalidParameterError parâmetro recusado localmente, antes do HTTP (também ValueError)
├── APIError a API respondeu com status de erro
│ ├── InvalidRequestError 400, 405, 410, 415, 422 e demais 4xx
│ │ └── DuplicateExternalIdError externalId repetido na emissão
│ ├── AuthenticationError 401
│ ├── PermissionDeniedError 403 (a mensagem diz qual chave a família usa)
│ ├── NotFoundError 404
│ ├── ConflictError 409
│ ├── RateLimitError 429 (retry_after)
│ └── ServerError 408 e 5xx
├── APIConnectionError falha de rede (phase)
│ └── APITimeoutError prazo de conexão, leitura ou total esgotado
├── ResponseTooLargeError resposta acima de max_response_bytes
├── UnexpectedResponseError resposta de sucesso que o SDK não consegue interpretar
├── InvoiceProcessingError a nota terminou em IssueFailed, CancelFailed ou Error
├── PollingTimeoutError o prazo de espera acabou antes de um estado final
└── SignatureVerificationError assinatura de webhook inválida

Atributos​

ExceçãoAtributos
APIError e subclassesstatus_code, message, error_code, request_id, trace_id, body (bytes), json_body, headers, outcome_unknown, external_id
RateLimitErroros de APIError + retry_after (segundos, ou None)
APIConnectionError, APITimeoutErrormessage, phase (FailurePhase.NOT_ESTABLISHED ou MAYBE_SENT), outcome_unknown, external_id
InvalidParameterErrormessage, param (nome do parâmetro recusado)
InvoiceProcessingErrorinvoice, flow_status, flow_message (a explicação da prefeitura)
PollingTimeoutErrorinvoice (a última observada, ou None), flow_status, elapsed
UnexpectedResponseErrorstatus_code, body, headers, request_id

request_id é o x-request-id da resposta: informe esse valor ao suporte. A mensagem já vem limpa (sem caracteres de controle, até 1.000 caracteres), extraída dos formatos de erro que a API usa.

from decimal import Decimal

from nfeio import APIError, InvalidRequestError, NfeClient

client = NfeClient()

try:
client.service_invoices.create(
"ID_DA_EMPRESA",
{"cityServiceCode": "10677", "description": "Teste", "servicesAmount": Decimal("10.00")},
external_id="pedido-3001",
)
except InvalidRequestError as erro:
# Validação da API: corrija o payload antes de tentar de novo.
print(erro.status_code, erro.message, erro.request_id)
print(erro.json_body) # corpo decodificado, quando é JSON
except APIError as erro:
print(type(erro).__name__, erro) # status, código e request_id já no str()

A regra: nunca reenviar uma emissão às cegas​

A API pode criar a nota e mesmo assim responder 500 ou 504, ou a conexão pode cair depois que o pedido chegou. Reenviar nesse caso emite a nota duas vezes. Por isso o SDK:

  • nunca repete um POST depois de timeout de leitura, conexão interrompida, 408, 429 ou 5xx. Só repete quando a falha aconteceu antes de conectar, porque aí nada saiu da máquina;
  • marca esses erros com outcome_unknown=True e o external_id usado;
  • devolve DuplicateExternalIdError quando a API recusa um externalId já usado. A primeira tentativa existe.

outcome_unknown=True quer dizer "não sabemos se a nota foi criada". Não é falha nem sucesso: é um estado a reconciliar. Idempotency-Key não resolve, porque a API ignora esse cabeçalho hoje.

Receita de reconciliação​

Sempre envie external_id e, diante de resultado incerto, procure a nota antes de tentar de novo:

from nfeio import (
APIConnectionError,
APIError,
DuplicateExternalIdError,
NfeClient,
ServiceInvoice,
)
from nfeio.types import ServiceInvoiceCreateParams


class EmissaoNaoRegistrada(Exception):
"""A API não registrou a emissão. Tente de novo mais tarde, com o mesmo external_id."""


def emitir(
client: NfeClient,
company_id: str,
external_id: str,
params: ServiceInvoiceCreateParams,
) -> ServiceInvoice:
try:
return client.service_invoices.create(company_id, params, external_id=external_id)
except DuplicateExternalIdError:
pass # uma tentativa anterior foi registrada: busque a nota
except (APIError, APIConnectionError) as erro:
if not erro.outcome_unknown:
raise # validação, chave inválida etc.: corrija antes de tentar de novo
nota = client.service_invoices.find_by_external_id(company_id, external_id, wait=30)
if nota is None:
raise EmissaoNaoRegistrada(external_id)
return nota

Como a receita funciona:

  1. DuplicateExternalIdError vem antes de APIError porque é subclasse dele.
  2. Erros com outcome_unknown=False (validação, 401, 403, 404) não criaram nota. Corrija a causa antes de tentar de novo.
  3. find_by_external_id(..., wait=30) repete a busca com backoff por até 30 segundos, cobrindo o atraso de indexação logo após o 202. Devolve a nota ou None. Uma resposta que não seja a lista vazia explícita da API levanta UnexpectedResponseError: o SDK prefere falhar a concluir "pode emitir de novo" por engano.
  4. Com None, a emissão não foi registrada. Tente de novo mais tarde com o mesmo external_id: se a primeira tentativa aparecer depois, a API recusa com DuplicateExternalIdError e a receita reconcilia, sem nota duplicada.
external_id é a sua garantia

Sem external_id, não há como reconciliar: a única informação sobre a nota criada é a que se perdeu com a resposta. Use um valor estável e único por venda (o id do pedido, da fatura ou da cobrança no seu sistema), nunca um UUID gerado a cada tentativa.

429 em emissão

RateLimitError num POST também vem com outcome_unknown=True: não há garantia de que o limitador recusou o pedido antes de processá-lo. Reconcilie antes de reenviar.

Erros de rede​

APIConnectionError.phase diz se o pedido pode ter saído:

phaseSignificadooutcome_unknown num POST
FailurePhase.NOT_ESTABLISHEDDNS, conexão recusada ou handshake TLS: nada saiu da máquinaFalse (o SDK já repetiu)
FailurePhase.MAYBE_SENTtimeout de leitura, conexão interrompida: o pedido pode ter chegadoTrue

Em GET, PUT e DELETE, que são seguros de repetir, o SDK tenta de novo sozinho até max_retries antes de levantar o erro.

Erros de espera​

wait, create_and_wait e cancel_and_wait têm dois erros próprios:

  • InvoiceProcessingError: a nota terminou em IssueFailed, CancelFailed ou Error. O motivo da prefeitura está em flow_message. A nota existe, com status de falha, e o external_id usado ficou registrado nela. Corrija a causa e emita uma nota nova, com outro external_id.
  • PollingTimeoutError: o prazo acabou antes de um estado final. A nota existe e continua sendo processada. Não emita de novo: consulte depois com retrieve ou wait.

Erros locais​

InvalidParameterError e ConfigurationError são levantados antes de qualquer requisição, e também são ValueError:

from nfeio import ConfigurationError, InvalidParameterError, NfeClient, Timeout

try:
Timeout(connect=10, read=60, total=0)
except ConfigurationError as erro:
print(erro) # timeout.total must be a finite number of seconds greater than zero

client = NfeClient(api_key="chave-de-exemplo", data_api_key="chave-de-dados-de-exemplo")
try:
client.lookups.cnpj("11.222.333/0001-00") # dígito verificador errado
except InvalidParameterError as erro:
print(erro.param) # cnpj
print(erro) # cnpj is not a valid CNPJ: '11.222.333/0001-00'

O SDK valida localmente ids, external_id, CNPJ (inclusive o alfanumérico), CPF, CEP, UF e datas. As consultas podem ser cobradas, então um documento inválido nunca chega à API.

Chave errada para o host​

Um 403 quase sempre é a chave da família errada (a principal numa consulta de CEP, ou a de dados na emissão). PermissionDeniedError diz qual parâmetro a família usa:

permission denied for this API family (address); it uses data_api_key — check that the right key was configured

Veja também​

NFE.io

A NFE.io é uma empresa de tecnologia que fornece soluções para automatizar e simplificar a emissão e gestão de notas fiscais eletrônicas. Com suas ferramentas, as empresas podem economizar tempo e reduzir erros, aumentando a eficiência e precisão do processo de emissão de notas fiscais.

Um dos principais cases de sucesso da NFE.io é a implementação da solução na empresa de transporte Rodonaves. Com a automatização da emissão e gestão de notas fiscais eletrônicas, a Rodonaves conseguiu reduzir em até 80% o tempo gasto nesse processo, o que se traduziu em uma significativa melhoria na eficiência operacional. Além disso, a empresa também conseguiu eliminar erros e atrasos na emissão de notas fiscais, o que melhorou a relação com seus clientes e aumentou a confiança dos órgãos fiscais.

Outro exemplo é a implementação da NFE.io na empresa de comércio eletrônico, a Loja Integrada. Com a automatização da emissão de notas fiscais, a Loja Integrada conseguiu aumentar a velocidade de emissão de notas em até 10 vezes, o que permitiu que a empresa atendesse a uma maior quantidade de clientes e, consequentemente, aumentar as suas vendas.

Além desses exemplos, a NFE.io também tem outros cases de sucesso com empresas de setores como indústria, construção, varejo e serviços, mostrando a versatilidade e eficácia da sua solução.

Em resumo, a NFE.io é uma empresa de tecnologia que oferece soluções para automatizar e simplificar a emissão e gestão de notas fiscais eletrônicas, ajudando as empresas a economizar tempo e reduzir erros, melhorando a eficiência e precisão do processo. Com cases de sucesso em diferentes setores, a NFE.io tem se destacado como uma empresa líder em automação fiscal.