Passo 4 · Erros e bloqueios
Toda falha de captura chega ao seu código de duas formas: como um erro no evento error do
componente <provvi-camera> (Tabela A) ou, por trás dele, como um erro HTTP de um dos endpoints
da API (Tabela B). Esta página é orientada a ação: para cada caso, o que fazer no seu app ou
o que orientar o usuário.
Alguns bloqueios (imagem estática, recaptura, divergência de localização) são gates de dissuasão de fraude (fraud deterrence). Lembre que a garantia do fluxo web é assinatura server-side ICP-Brasil A1 sobre os bytes recebidos — não há âncora de hardware device-side. Esses gates elevam o custo do ataque; não substituem a cadeia de prova com hardware do SDK mobile.
Como tratar (padrão)
Seção intitulada “Como tratar (padrão)”Trate primeiro por classe (instanceof) para o caso amplo, depois por código
(error.code) para o granular. O objeto do erro traz error.code, error.details e
error.toJSON() (sem stack trace, seguro para telemetria).
import { CODES, ProvviBrowserError, ProvviLicenseError, ProvviStreamError,} from '@provvi/web-sdk';
camera.addEventListener('error', (e) => { const { error } = e.detail; // ProvviError com .code e .details
// 1) Por classe — trata o caso amplo if (error instanceof ProvviBrowserError) { mostrarQrParaCelular(); // 2002 — abra no celular return; } if (error instanceof ProvviLicenseError) { // error.details.reason: INVALID_KEY | REVOKED | EXPIRED | // VOLUME_EXCEEDED | CLIENT_SUSPENDED | ORIGIN_NOT_ALLOWED mostrarErroLicenca(error.details?.reason); return; } if (error instanceof ProvviStreamError) { pedirPermissaoCamera(); // 3001 — permita a câmera return; }
// 2) Por código — trata o granular switch (error.code) { case CODES.TOKEN_REQUIRED: // 5002 — bug de integração console.error('open() foi chamado sem token'); break; case CODES.SESSION_FETCH_FAILED: // 4001 — sessão inválida recriarSessao(); break; case CODES.INGEST_FAILED: // 4002 — mapeie pelo HTTP tratarIngest(error.details?.status); break; default: reportarTelemetria(error.toJSON?.() ?? error); }});O código INGEST_FAILED (4002) embrulha os erros HTTP da captura: o status real fica em
error.details.status. Mapeie-o para a ação do usuário (Tabela B, seção POST /web/ingest):
function tratarIngest(status) { switch (status) { case 400: // imagem estática — peça uma captura ao vivo mostrar('Aponte para o objeto real e capture ao vivo (não use uma foto pronta).'); break; case 403: // license, relógio ou nonce — inspecione o corpo mostrar('Verifique a data e a hora do dispositivo e recarregue a página.'); break; case 404: case 410: // sessão inválida ou expirada — crie outra recriarSessao(); break; case 409: // não foi possível processar — refaça a captura mostrar('Não foi possível processar esta captura. Tente novamente.'); break; case 422: // recaptura ou localização — objeto físico + permitir GPS mostrar('Fotografe o objeto físico e permita o acesso à localização.'); break; case 502: // transitório — tente de novo mostrar('Falha temporária. Tente novamente em instantes.'); break; default: mostrar('Não foi possível concluir a captura.'); }}Tabela A — Erros no evento error do componente
Seção intitulada “Tabela A — Erros no evento error do componente”Estes são os erros com throw-site real hoje. Cada um chega em e.detail.error.
code |
Nome | Quando ocorre | O que fazer |
|---|---|---|---|
2002 |
BROWSER_NOT_SUPPORTED |
Navegador fora da allowlist. Só Safari mobile ou Chrome mobile passam; desktop, Firefox e Chrome/Edge/Opera no iOS são bloqueados. | Use camera.isUserAgentSupported() antes de abrir e conduza o usuário no seu fluxo quando não for suportado (ex.: orientá-lo a abrir no celular). |
3001 |
STREAM_OPEN_FAILED |
Falha ao abrir a câmera — permissão negada ou câmera em uso por outro app. A câmera é obrigatória. | Peça ao usuário para permitir a câmera e fechar apps que a estejam usando; ofereça repetir. |
5002 |
TOKEN_REQUIRED |
open() foi chamado sem token. |
Bug de integração — passe o token da sessão criada no backend (Passo 1). |
8001 |
LICENSE_KEY_MISSING |
licenseKey não foi informada em useProvviCamera() nem em open(). |
Bug de integração — informe a license key pvv_* do frontend. |
8002 |
LICENSE_INVALID |
Licença rejeitada na validação. O motivo está em error.details.reason. |
Depende do reason: EXPIRED/VOLUME_EXCEEDED → renovar/ampliar; REVOKED/CLIENT_SUSPENDED → contatar a Provvi; ORIGIN_NOT_ALLOWED → domínio não autorizado (ver Passo 6). |
8003 |
LICENSE_NETWORK_ERROR |
Falha de rede ao validar a licença. | Verifique a conexão do usuário e ofereça repetir. |
4001 |
SESSION_FETCH_FAILED |
Não foi possível carregar a sessão (não encontrada, expirada, já usada ou muitas recargas). | Crie uma nova sessão no backend e reabra o componente. Detalhe HTTP na Tabela B, seção GET /web/sessions/:token. |
4002 |
INGEST_FAILED |
Falha ao enviar a captura (inclui falha de upload). O status HTTP está em error.details.status. |
Mapeie details.status pela Tabela B, seção POST /web/ingest. |
| — | reservados (não ocorrem hoje) | 1000, 3002, 5001, 6001, 6002, 6003, 7001 existem em CODES mas não têm throw-site atual. |
Não trate como comportamento; cubra-os apenas no default de telemetria. |
Tabela B — Erros HTTP na captura
Seção intitulada “Tabela B — Erros HTTP na captura”Os corpos abaixo são os literais retornados pela API. O componente chama GET /web/sessions e
POST /web/ingest internamente e os expõe via os códigos 4001/4002 (acima). A criação de
sessão você mesmo faz no backend (Passo 1).
POST /web/sessions — criação da sessão (backend)
Seção intitulada “POST /web/sessions — criação da sessão (backend)”| HTTP | Corpo | O que fazer |
|---|---|---|
401 |
{ "error": "API key inválida ou ausente" } (variações: "...ou revogada", "...apenas pvv_api_* aceito") |
Confira o header x-api-key: pvv_api_... do seu backend. Nunca envie a x-api-key ao frontend. |
403 |
{ "error": "License inválida para esta API key" } |
A license_key do corpo não pertence a esta x-api-key. Use a license emitida para a mesma conta. |
429 |
{ "error": "Limite de criação de sessões atingido..." } |
Reduza a taxa de criação de sessões e aplique backoff. Cada sessão é única — não recrie desnecessariamente. |
503 |
transitório (contador indisponível, fail-closed) | Repita a criação com backoff exponencial. Não trate como falha permanente. |
GET /web/sessions/:token — chamado pelo componente (chega como 4001)
Seção intitulada “GET /web/sessions/:token — chamado pelo componente (chega como 4001)”| HTTP | Corpo | O que fazer |
|---|---|---|
404 |
{ "error": "Sessão não encontrada" } |
Token inválido ou nunca emitido. Crie uma nova sessão no backend. |
410 |
{ "error": "Sessão expirada ou já utilizada" } |
Sessões são single-use e têm validade limitada. Gere uma sessão nova. |
429 |
{ "error": "Limite de tentativas atingido para esta sessão" } |
A página foi recarregada vezes demais. Oriente o usuário a recomeçar com uma sessão nova. |
POST /web/ingest — chamado pelo componente (chega como 4002, details.status)
Seção intitulada “POST /web/ingest — chamado pelo componente (chega como 4002, details.status)”| HTTP | Corpo | O que fazer |
|---|---|---|
400 |
{ "error": "Imagem estática detectada..." } |
Foi enviada uma imagem sem sinal de captura ao vivo. Oriente o usuário a apontar para o objeto real e capturar pela câmera. |
403 |
{ "error": "..." } — licença revalidada (revogada/suspensa/expirada) |
A licença deixou de ser válida durante a sessão. Trate como licença inválida (ver Tabela A, 8002). |
403 |
{ "error": "Relógio do dispositivo fora de sincronia..." } |
Oriente o usuário a verificar a data e a hora do dispositivo e capturar de novo. |
403 |
{ "error": "..." } — nonce ausente/inválido |
Oriente o usuário a recarregar a página para renovar a sessão. |
404 |
{ "error": "Sessão não encontrada" } |
Sessão inexistente. Crie uma nova. |
409 |
{ "error": "Esta captura não pôde ser processada." } |
Corpo opaco por design. Oriente o usuário a repetir a captura; se persistir, use uma sessão nova. |
410 |
{ "error": "Sessão expirada ou já utilizada" } |
Sessão fora de validade ou já usada. Gere uma nova. |
422 |
{ "error": "Imagem rejeitada — possível recaptura", "recapture_risk": ... } |
Foto de tela/foto-de-foto detectada. Oriente o usuário a fotografar o objeto físico, sem telas no enquadramento. |
422 |
{ "error": "...", "block_reason": "..." } — divergência ou ausência de localização |
Só ocorre com verificação de GPS em modo enforce. Oriente o usuário a permitir o acesso à localização. Ajuste o modo em Passo 6 · Configuração. |
502 |
{ "error": "Erro de assinatura: ..." } |
Falha transitória na assinatura. Ofereça repetir a captura em instantes. |
Os contratos HTTP completos (payloads, campos e envelopes) estão na Referência da API Web; a API do componente e os códigos, na Referência do Web SDK.