Tratamento de Erros
Guia completo para entender e tratar erros da API Lucent KYC.
📊 Códigos HTTP
| Código | Status | Significado | Ação Recomendada |
|---|---|---|---|
| 200 | OK | Sucesso | Processar resposta |
| 201 | Created | Recurso criado | Confirmar criação |
| 400 | Bad Request | Dados inválidos na requisição | Verificar formato dos dados |
| 401 | Unauthorized | Não autenticado | Verificar/renovar token |
| 403 | Forbidden | Sem permissão | Verificar tenant/scopes |
| 404 | Not Found | Recurso não encontrado | Verificar ID/URL |
| 422 | Unprocessable Entity | Erro de validação | Verificar esquema Zod |
| 429 | Too Many Requests | Rate limit excedido | Implementar retry com backoff |
| 500 | Internal Server Error | Erro no servidor | Retry + contatar suporte |
| 503 | Service Unavailable | Serviço indisponível | Aguardar 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
Authorizationmal 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:
- Verificar se registro existe em
profilescomuser_id - Confirmar que
tenant_idestá preenchido - 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_datasetssem registros ativos para este tenantbasic_datanã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
readtentando fazerPOST/PATCH/DELETE - Endpoint administrativo requerendo scope
admin
Solução:
- Criar nova API Key com scope adequado
- Mapear operações por scope:
read: GET apenaswrite: 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:
- Retry com exponential backoff
- Se persistir, usar modo mock temporariamente
- 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.okantes 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
- Autenticação - Detalhes sobre JWT e API Keys
- Melhores Práticas - Estratégias de resiliência
- REST API v1 - Endpoints para integração externa
Precisa de ajuda? Entre em contato: suporte@lucentminds.com