Tratamento de Erros Padronizado - C-Suite
Visão Geral
Este documento descreve o padrão de tratamento de erros padronizado para todo o ecossistema C-Suite. O objetivo é ter respostas de erro consistentes e informativas em todos os apps.
Módulo de Erros
Localização
O módulo principal está em: common_errors.py (raiz do projeto)
Uso Básico
from common_errors import CSuiteError, ErrorCode
# Lançar erro padronizado
raise CSuiteError(
code=ErrorCode.CUSTOMER_NOT_FOUND,
message="Cliente não encontrado",
status_code=404,
details={"customer_id": 123}
)
Formato de Resposta Padronizado
Todas as respostas de erro seguem este formato:
{
"error": {
"code": "ERR_4000",
"message": "Cliente não encontrado",
"timestamp": "2025-12-01T10:30:00Z",
"details": {
"customer_id": 123
}
}
}
Campos
- code: Código de erro padronizado (ex:
ERR_4000) - message: Mensagem de erro legível para humanos
- timestamp: Timestamp ISO 8601 do erro
- details: Objeto opcional com detalhes adicionais
Códigos de Erro Padronizados
Erros Gerais (1xxx)
| Código | Descrição | HTTP Status |
|---|---|---|
ERR_1000 |
Erro interno | 500 |
ERR_1001 |
Erro de validação | 400 |
ERR_1002 |
Não autorizado | 401 |
ERR_1003 |
Acesso negado | 403 |
ERR_1004 |
Recurso não encontrado | 404 |
ERR_1005 |
Conflito | 409 |
ERR_1006 |
Rate limit excedido | 429 |
ERR_1007 |
Serviço indisponível | 503 |
ERR_1008 |
Timeout | 504 |
Erros de Banco de Dados (2xxx)
| Código | Descrição | HTTP Status |
|---|---|---|
ERR_2000 |
Erro de conexão com banco | 503 |
ERR_2001 |
Erro em query | 500 |
ERR_2002 |
Erro em transação | 500 |
ERR_2003 |
Violação de constraint | 409 |
Erros de Serviços Externos (3xxx)
| Código | Descrição | HTTP Status |
|---|---|---|
ERR_3000 |
Erro em serviço externo | 502 |
ERR_3001 |
Timeout em serviço externo | 504 |
ERR_3002 |
Serviço externo indisponível | 503 |
Erros de Negócio 4C (4xxx)
| Código | Descrição | HTTP Status |
|---|---|---|
ERR_4000 |
Cliente não encontrado | 404 |
ERR_4001 |
Candidato inválido | 400 |
ERR_4002 |
Score de intent muito baixo | 400 |
ERR_4003 |
Margem insuficiente | 400 |
ERR_4004 |
Violação de opt-out | 403 |
ERR_4005 |
Limite de contatos excedido | 429 |
ERR_4006 |
Janela de silêncio ativa | 429 |
ERR_4007 |
Nenhuma oferta válida | 400 |
Erros de Negócio C-Suite (5xxx)
| Código | Descrição | HTTP Status |
|---|---|---|
ERR_5000 |
Organização não encontrada | 404 |
ERR_5001 |
Agente não encontrado | 404 |
ERR_5002 |
Política não encontrada | 404 |
ERR_5003 |
Política inválida | 400 |
ERR_5004 |
Métrica não encontrada | 404 |
ERR_5005 |
Alerta não encontrado | 404 |
Erros de Integração (6xxx)
| Código | Descrição | HTTP Status |
|---|---|---|
ERR_6000 |
Erro de sincronização | 500 |
ERR_6001 |
Erro ao carregar políticas | 500 |
ERR_6002 |
Erro ao sincronizar métricas | 500 |
Integração com FastAPI
Configurar Exception Handler Global
from fastapi import FastAPI
from common_errors import error_handler
app = FastAPI()
# Adiciona handler global
app.add_exception_handler(Exception, error_handler)
Usar em Endpoints
from fastapi import APIRouter
from common_errors import CSuiteError, ErrorCode, not_found_error
router = APIRouter()
@router.get("/customers/{customer_id}")
async def get_customer(customer_id: int):
customer = await find_customer(customer_id)
if not customer:
raise not_found_error("Cliente", customer_id)
return customer
Funções de Conveniência
Erro 404 - Not Found
from common_errors import not_found_error
raise not_found_error("Cliente", customer_id=123)
# Retorna: {"error": {"code": "ERR_1004", "message": "Cliente não encontrado: 123", ...}}
Erro 400 - Validation Error
from common_errors import validation_error
raise validation_error(
"Dados inválidos",
details={"field": "email", "reason": "formato inválido"}
)
Erro 401 - Unauthorized
from common_errors import unauthorized_error
raise unauthorized_error("Token inválido ou expirado")
Erro 403 - Forbidden
from common_errors import forbidden_error
raise forbidden_error("Você não tem permissão para acessar este recurso")
Erro 409 - Conflict
from common_errors import conflict_error
raise conflict_error(
"Política já existe",
details={"rule_key": "min_margin", "organization_id": 1}
)
Erro 503 - Service Unavailable
from common_errors import service_unavailable_error
raise service_unavailable_error(
"Feature Service",
details={"endpoint": "/features/customer", "timeout": 5.0}
)
Exemplos de Uso
Exemplo 1: Erro de Cliente Não Encontrado
@router.get("/customers/{customer_id}")
async def get_customer(customer_id: int):
customer = db.query(Customer).filter(Customer.id == customer_id).first()
if not customer:
raise CSuiteError(
code=ErrorCode.CUSTOMER_NOT_FOUND,
message=f"Cliente {customer_id} não encontrado",
status_code=404,
details={"customer_id": customer_id}
)
return customer
Exemplo 2: Erro de Validação
@router.post("/decide")
async def decide(request: DecideRequest):
if not request.customer_id:
raise validation_error(
"customer_id é obrigatório",
details={"field": "customer_id", "type": "required"}
)
if request.customer_id < 1:
raise validation_error(
"customer_id deve ser positivo",
details={"field": "customer_id", "value": request.customer_id}
)
# ... resto do código
Exemplo 3: Erro de Serviço Externo
async def call_feature_service(customer_id: int):
try:
async with httpx.AsyncClient() as client:
response = await client.post(
f"{FEATURE_URL}/features/customer",
json={"customer_id": customer_id},
timeout=5.0
)
response.raise_for_status()
return response.json()
except httpx.TimeoutException:
raise service_unavailable_error(
"Feature Service",
details={"timeout": 5.0, "customer_id": customer_id}
)
except httpx.HTTPStatusError as e:
raise CSuiteError(
code=ErrorCode.EXTERNAL_SERVICE_ERROR,
message=f"Erro ao chamar Feature Service: {e.response.status_code}",
status_code=502,
details={"service": "feature_service", "http_status": e.response.status_code}
)
Exemplo 4: Erro de Banco de Dados
async def get_organization(org_id: int):
try:
org = db.query(Organization).filter(Organization.id == org_id).first()
if not org:
raise not_found_error("Organização", org_id)
return org
except Exception as e:
logger.exception(f"Erro ao buscar organização {org_id}")
raise CSuiteError(
code=ErrorCode.DB_QUERY_ERROR,
message="Erro ao consultar banco de dados",
status_code=500,
details={"organization_id": org_id},
cause=e
)
Migração de Código Existente
Antes (sem padrão)
from fastapi import HTTPException
if not customer:
raise HTTPException(status_code=404, detail="Cliente não encontrado")
Depois (padronizado)
from common_errors import not_found_error
if not customer:
raise not_found_error("Cliente", customer_id)
Ou usando CSuiteError diretamente
from common_errors import CSuiteError, ErrorCode
if not customer:
raise CSuiteError(
code=ErrorCode.CUSTOMER_NOT_FOUND,
message=f"Cliente {customer_id} não encontrado",
status_code=404,
details={"customer_id": customer_id}
)
Logging
Todos os erros são automaticamente logados com:
- Código de erro
- Mensagem
- Status HTTP
- Detalhes (se fornecidos)
- Traceback (se houver causa)
Ambiente de Desenvolvimento vs Produção
Desenvolvimento
Em desenvolvimento (ENVIRONMENT=development), erros incluem:
- Mensagem completa da exceção
- Tipo da exceção
- Traceback completo
Produção
Em produção, erros são genéricos:
- Mensagem genérica ("Erro interno do servidor")
- Sem detalhes técnicos
- Traceback apenas em logs
Próximos Passos
- ✅ Módulo de erros padronizados criado
- ⏳ Migrar todos os apps para usar
common_errors - ⏳ Adicionar exception handlers em todos os apps FastAPI
- ⏳ Criar testes para tratamento de erros
- ⏳ Documentar códigos de erro específicos por app