Glossário de Códigos de Erro¶
Referência oficial dos erros da integração Nayax ↔︎ Saipos, jobs de outbox e camada HTTP/Prisma. Use isto para padronizar tratamento, observabilidade e DX.
Convenções¶
error: título curto e traduzido.message: detalhe legível (pode ser oculto em produção).code: identificador técnico quando existir (ex.:P2002,901).status: HTTP status code.requestId: correlação nos logs.timestamp: ISO UTC.
Sumário¶
- HTTP genéricos
- Prisma
- Autenticação/Parceiro (Saipos)
- Aplicação (regras/negócio)
- Jobs de Outbox
- Formato de resposta de erro
- Exemplos
- Matriz de decisões (cliente)
- Observabilidade
HTTP genéricos¶
| status | error | Quando | Ação cliente |
|---|---|---|---|
| 400 | Bad Request | Payload inválido, domínio inválido, duplicidade de evento em produção | Corrigir e reenviar |
| 401 | Unauthorized | Sem autenticação | Autenticar |
| 403 | Forbidden | Autenticado mas sem permissão | Revisar permissões |
| 404 | Not Found | Recurso não existe | Validar IDs |
| 409 | Conflito | Violação de unicidade (espelha P2002) | Ajustar dados |
| 422 | Unprocessable Entity | Estrutura válida, regra de negócio não satisfeita | Corrigir domínio |
| 500 | Erro interno do servidor | Exceção não tratada | Retry com backoff |
| 503 | Service Unavailable (opcional) | Dependência externa indisponível | Retry com backoff |
Handler de fallback: erros sem
statusCodeviram500e ocultammessagefora deNODE_ENV=development.
Prisma¶
| code | status | error | Descrição | Exemplo prático |
|---|---|---|---|---|
| P2002 | 409 | Conflito | Unique constraint violation | (scheduleId,userId,date,shiftId) |
| P2025 | 404 | Não encontrado | Registro alvo não existe para update/delete | update() em ID inexistente |
Tratamento direto no
errorHandler:
P2002→409 { error: 'Conflito', message: 'Registro duplicado' }P2025→404 { error: 'Não encontrado', message: 'Registro não encontrado' }
Autenticação/Parceiro (Saipos)¶
Alguns erros do parceiro usam códigos próprios no payload:
| code | status | error | Significado | Ação |
|---|---|---|---|---|
| 901 | 401 | Auth inválida (Saipos) | Token/credenciais inválidos ou expirados | Invalidar cache e renovar credenciais |
| 902 | 401 | Auth ausente (Saipos) | Credenciais não informadas | Configurar idPartner/secret |
O
JobsProcessorinvalidatokenCacheautomaticamente quando detecta 401/901/902.
Aplicação (regras/negócio)¶
Erros lançados no fluxo de ingestão e regras:
| status | error | Quando | Origem |
|---|---|---|---|
| 400 | Autenticação necessária | Requisição sem actor | NayaxService.ingestAndEnqueue |
| 400 | Você não possui um restaurante cadastrado | Usuário autenticado sem vínculo userRestaurant como OWNER | NayaxService.ingestAndEnqueue |
| 400 | Transação {transactionKey} já foi processada | Evento duplicado em produção | Idempotência por transactionKey |
| 200 | (ignorado deliberadamente) | Evento duplicado de teste (STORE1001/isTestTransaction) é ignorado | Idempotência relaxada |
Jobs de Outbox¶
Estados e transições administrados pelo JobsProcessor (cron */20 * * * * *):
Estados¶
| status | Descrição | Disparo/Transição |
|---|---|---|
PENDING | Criado, aguardando processamento | criação do evento Nayax |
PROCESSING | Em execução por um worker | claim otimista via updateMany |
SENT | Enviado com sucesso ao parceiro | Após createOrder/cancelOrder OK |
FAILED | Falha temporária; será re-tentado com backoff | Erros 5xx, network, timeouts |
DLQ | Falha permanente; não re-tentará | 400/422 domínio inválido no parceiro |
Política de Backoff (exponencial com jitter)¶
| Tentativa (n) | Base (ms) | Janela com jitter (~±20%) |
|---|---|---|
| 1 | 30.000 | 24.000 – 36.000 |
| 2 | 120.000 | 96.000 – 144.000 |
| 3 | 300.000 | 240.000 – 360.000 |
| 4 | 900.000 | 720.000 – 1.080.000 |
| 5 | 1.800.000 | 1.440.000 – 2.160.000 |
| ≥6 | 3.600.000 | 2.880.000 – 4.320.000 |
Reaper: jobs travados em
PROCESSINGalém do TTL (PROCESSING_TTL_MS, default 120.000 ms) são revertidos paraFAILEDcom agendamento do próximo run.
Formato de resposta de erro¶
HTTP (REST)¶
{
"error": "Conflito",
"message": "Registro duplicado",
"code": "P2002",
"status": 409,
"requestId": "ac76763c9d2e",
"timestamp": "2025-10-28T10:57:12.931Z"
}
Em desenvolvimento (
NODE_ENV=development), mensagens internas podem incluirstacke detalhes adicionais.
Log estruturado do Job (falha)¶
{
"id": "job-uuid",
"attempts": 3,
"nextStatus": "FAILED",
"backoff": 300000,
"status": 502,
"raw": { "partnerMessage": "gateway timeout" },
"message": "Network error"
}
Exemplos¶
1) P2002 (unicidade)¶
Entrada: criação de agendamento duplicado Saída (HTTP 409):
2) Duplicidade de transação Nayax (produção)¶
Saída (HTTP 400):
3) Duplicidade de transação Nayax (teste)¶
Saída: 200 OK e log "Ignorando duplicata" (idempotência relaxada).
4) Saipos 901/902 (auth)¶
Saída (HTTP 401):
Ação do worker: limpar
tokenCachee reautenticar.
5) DLQ por erro 422 do parceiro¶
Transição: PROCESSING → DLQ Registro no job:
{
"status": "DLQ",
"lastError": {
"msg": "Campo obrigatório ausente: cod_store",
"status": 422,
"data": { "error": "Unprocessable Entity" }
}
}
Matriz de decisões (cliente)¶
| status/parceiro | Deve re-tentar? | Deve corrigir payload? | Observação |
|---|---|---|---|
| 400 / 422 | ❌ | ✅ | Erro de domínio |
| 401 / 901 / 902 | ✅ (após auth) | ⚠️ | Renove credenciais |
| 404 | ❌ | ✅ | Recurso inexistente |
| 409 | ❌ | ✅ | Conflito de unicidade |
| 5xx / timeout | ✅ (backoff) | ❌ | Falha transitória |
Observabilidade¶
Para correlação eficiente:
- Logs estruturados contêm
requestId,eventKey,job.id,status,action,attempts. - Console hard logs no
JobsProcessor(pré/envio e erro) garantem visibilidade fora doLogger. - Campos recomendados para dashboards: taxa de
DLQ, latência, tentativas médias, distribuições destatus, toplastError.msg.
Anexos (trechos relevantes)¶
Filtro de erros no errorHandler¶
// Prisma
if (err.code === 'P2002') return res.status(409).json({ error: 'Conflito', message: 'Registro duplicado', code: 'P2002' });
if (err.code === 'P2025') return res.status(404).json({ error: 'Não encontrado', message: 'Registro não encontrado', code: 'P2025' });
// Custom
if (err.statusCode) return res.status(err.statusCode).json({ error: err.message });
// Genérico
return res.status(500).json({
error: 'Erro interno do servidor',
message: process.env.NODE_ENV === 'development' ? err.message : undefined,
});
Classificação no JobsProcessor (resumo)¶
400/422→DLQ401/901/902→ limpa token, re-tenta- Demais erros →
FAILED+ backoff exponencial (com jitter)
Takeaway: DLQ para erros de domínio, FAILED para transitórios, idempotência rígida em produção e relaxada em teste. Ajuste, reprocessa, escala.