Pular para o conteúdo principal

Tratamento de Erros

Guia completo para entender e tratar erros da API Lucent KYC.

📊 Códigos HTTP

CódigoStatusSignificadoAção Recomendada
200OKSucessoProcessar resposta
201CreatedRecurso criadoConfirmar criação
400Bad RequestDados inválidos na requisiçãoVerificar formato dos dados
401UnauthorizedNão autenticadoVerificar/renovar token
403ForbiddenSem permissãoVerificar tenant/scopes
404Not FoundRecurso não encontradoVerificar ID/URL
422Unprocessable EntityErro de validaçãoVerificar esquema Zod
429Too Many RequestsRate limit excedidoImplementar retry com backoff
500Internal Server ErrorErro no servidorRetry + contatar suporte
503Service UnavailableServiço indisponívelAguardar e retry

🚨 Erros Comuns

1. Erros de Autenticação (401)

Token JWT Inválido

{
"error": "Invalid token",
"message": "Token JWT inválido ou malformado"
}

Causas:

  • Token corrompido ou incompleto
  • Token de outro ambiente (dev/prod)
  • Header Authorization mal formatado

Solução:

// ❌ Errado
headers: { 'Authorization': token }

// ✅ Correto
headers: { 'Authorization': `Bearer ${token}` }

Token Expirado

{
"error": "Token expired",
"message": "Token JWT expirou. Renove sua sessão."
}

Causas:

  • Token com mais de 1 hora (padrão)
  • Sessão não renovada

Solução:

// Renovar token automaticamente
supabase.auth.onAuthStateChange((event, session) => {
if (event === 'TOKEN_REFRESHED') {
const newToken = session?.access_token;
// Atualizar token nas requisições
updateApiToken(newToken);
}
});

// Renovar manualmente
async function refreshToken() {
const { data, error } = await supabase.auth.refreshSession();
if (data.session) {
return data.session.access_token;
}
// Redirect para login
window.location.href = '/login';
}

2. Erros de Permissão (403)

Usuário Sem Tenant

{
"error": "User has no tenant",
"message": "Usuário não está associado a nenhum tenant"
}

Causas:

  • Usuário criado mas não associado a tenant
  • Perfil não encontrado na tabela profiles

Solução:

  1. Verificar se registro existe em profiles com user_id
  2. Confirmar que tenant_id está preenchido
  3. Contatar administrador para associar a tenant

Dataset Não Autorizado

{
"error": "Tenant has no enabled datasets for this query",
"message": "Tenant não tem datasets habilitados para esta consulta"
}

Causas:

  • Todos os datasets solicitados não autorizados
  • tenant_datasets sem registros ativos para este tenant
  • basic_data não disponível como fallback

Solução:

// Antes de consultar, verificar datasets disponíveis
const datasets = await fetch('/api/datasets', {
headers: { 'Authorization': `Bearer ${token}` }
});

// Usar apenas datasets autorizados
const authorizedDatasets = datasets.data
.filter(d => d.is_enabled)
.map(d => d.api_name)
.join(',');

Scope Insuficiente (API Key)

{
"error": "Insufficient permissions",
"message": "API Key não tem permissão para esta operação. Scope necessário: write"
}

Causas:

  • API Key com scope read tentando fazer POST/PATCH/DELETE
  • Endpoint administrativo requerendo scope admin

Solução:

  • Criar nova API Key com scope adequado
  • Mapear operações por scope:
    • read: GET apenas
    • write: GET + POST (consultas)
    • admin: Todas as operações

3. Erros de Validação (422)

CPF/CNPJ Inválido

{
"error": "Validation error",
"details": {
"cpf": "CPF deve ter 11 dígitos"
}
}

Validação de CPF:

function validateCPF(cpf: string): boolean {
// Remover formatação
const cleaned = cpf.replace(/\D/g, '');

// Deve ter 11 dígitos
if (cleaned.length !== 11) return false;

// Não pode ser sequência repetida
if (/^(\d)\1{10}$/.test(cleaned)) return false;

// Validar dígitos verificadores
// (implementação completa disponível no frontend)
return validateCPFDigits(cleaned);
}

Validação de CNPJ:

function validateCNPJ(cnpj: string): boolean {
const cleaned = cnpj.replace(/\D/g, '');

if (cleaned.length !== 14) return false;
if (/^(\d)\1{13}$/.test(cleaned)) return false;

return validateCNPJDigits(cleaned);
}

Campos Obrigatórios Ausentes

{
"error": "Validation error",
"details": {
"datasets": "Campo obrigatório"
}
}

Solução: Verificar esquema Zod antes de enviar:

import { z } from 'zod';

const cpfQuerySchema = z.object({
cpf: z.string().regex(/^\d{11}$/, 'CPF deve ter 11 dígitos'),
datasets: z.string().min(1, 'Datasets é obrigatório'),
useMockData: z.boolean().optional()
});

// Validar antes de enviar
const result = cpfQuerySchema.safeParse(formData);
if (!result.success) {
console.error('Erros de validação:', result.error.errors);
return;
}

// Enviar requisição
await queryCPF(result.data);

4. Rate Limiting (429)

{
"error": "Rate limit exceeded",
"message": "Limite de 100 requisições por minuto excedido",
"retry_after": 60
}

Limites:

  • JWT: 100 req/min por usuário
  • API Key: 100 req/min por chave
  • Batch: Conta como 1 req (não n reqs)

Estratégia de Retry:

async function fetchWithRetry(
url: string,
options: RequestInit,
maxRetries = 3
): Promise<Response> {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url, options);

if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
const delay = retryAfter ? parseInt(retryAfter) * 1000 : 60000;

console.warn(`Rate limit hit. Retry após ${delay}ms`);
await sleep(delay);
continue;
}

return response;
}

throw new Error('Max retries exceeded');
}

function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}

5. Erros de Servidor (500)

Falha no Provedor de Dados

{
"error": "Failed to fetch data from provider",
"details": {
"message": "API externa indisponível",
"status": 503
}
}

Estratégia:

  1. Retry com exponential backoff
  2. Se persistir, usar modo mock temporariamente
  3. Reportar ao suporte
async function retryWithBackoff<T>(
fn: () => Promise<T>,
maxRetries = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error: any) {
if (error.status === 500 && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
console.log(`Retry ${i + 1} após ${delay}ms`);
await sleep(delay);
} else {
throw error;
}
}
}
throw new Error('Max retries exceeded');
}

🛠️ Classe de Tratamento de Erros

export class LucentKYCError extends Error {
constructor(
public status: number,
public error: string,
public details?: any
) {
super(error);
this.name = 'LucentKYCError';
}

static async fromResponse(response: Response): Promise<LucentKYCError> {
const data = await response.json().catch(() => ({}));
return new LucentKYCError(
response.status,
data.error || response.statusText,
data.details || data.message
);
}

isAuthError(): boolean {
return this.status === 401 || this.status === 403;
}

isValidationError(): boolean {
return this.status === 422;
}

isRateLimitError(): boolean {
return this.status === 429;
}

isServerError(): boolean {
return this.status >= 500;
}

shouldRetry(): boolean {
return this.isRateLimitError() || this.isServerError();
}
}

Uso:

async function queryCPF(cpf: string, datasets: string, token: string) {
const response = await fetch('/api/kyc/cpf', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ cpf, datasets })
});

if (!response.ok) {
const error = await LucentKYCError.fromResponse(response);

if (error.isAuthError()) {
// Redirecionar para login ou renovar token
handleAuthError(error);
} else if (error.isValidationError()) {
// Mostrar erros de validação ao usuário
showValidationErrors(error.details);
} else if (error.shouldRetry()) {
// Implementar retry
return retryWithBackoff(() => queryCPF(cpf, datasets, token));
}

throw error;
}

return response.json();
}

📋 Checklist de Tratamento de Erros

Implementação Básica:

  • ✅ Verificar response.ok antes de processar
  • ✅ Tratar 401 (renovar token/redirect login)
  • ✅ Tratar 403 (verificar permissões)
  • ✅ Tratar 422 (mostrar erros de validação)
  • ✅ Logs de erro estruturados

Implementação Avançada:

  • ✅ Retry com exponential backoff para 429/500
  • ✅ Circuit breaker para falhas consecutivas
  • ✅ Fallback para modo mock em emergências
  • ✅ Monitoramento de taxa de erro
  • ✅ Alertas para erros críticos

🔗 Recursos Adicionais


Precisa de ajuda? Entre em contato: suporte@lucentminds.com