Обробка помилок і коди статусів
1) Навіщо стандартизувати помилки
Єдиний контракт помилок прискорює налагодження клієнтів, знижує помилкові ретраї і робить RCA відтворюваним. Хороша система:- передбачувано кодує тип проблеми,
- дає клієнту дійсні підказки (що робити далі),
- захищає від витоку внутрішніх деталей,
- сумісна з ретраями та ідемпотентністю.
2) Принципи дизайну
1. Одна схема помилок для всіх сервісів (REST/GraphQL/gRPC/webhooks).
2. Чітка семантика ретраїв: які коди ретраїти, які - ні.
3. Fail-closed на write-операціях: краще 4xx/5xx, ніж тиха неконсистентність.
4. Без витоків: не розкривати SQL, стеки, конфіги, внутрішні ID.
5. Трасування: завжди повертайте'trace _ id '/' correlation _ id'.
6. Локалізація повідомлень - опціонально, але коди і «reason» залишаються стабільними.
3) Єдиний формат (Problem Details/JSON)
Рекомендований базовий формат (сумісний з RFC 7807):json
{
"type": "https://errors.example.com/auth/invalid-token",
"title": "Invalid access token",
"status": 401,
"code": "AUTH_INVALID_TOKEN",
"detail": "Token expired or signature invalid.",
"instance": "/api/v1/payments/12345",
"trace_id": "01HX3...ABC",
"hint": "Obtain a new token via OAuth2 refresh.",
"meta": {
"scope": "payments:write",
"policy": "deny-by-default"
}
}
Пояснення:
- 'type'- стабільний URL-ідентифікатор класу помилок.
- «code» - короткий машинний код домену (стабільний між релізами).
- 'hint'- що робити клієнту (повтор, оновити токен, змінити параметри).
- «meta» - безпечні деталі (без секретів і PII).
4) Карта кодів статусів (мінімальний набір)
Автентифікація/авторизація
400 Bad Request - структурна валідація/схема.
401 Unauthorized - ні/неваліден токен. Додавайте'WWW-Authenticate'.
403 Forbidden - автентифікований, але немає прав/політики відмовили.
404 Not Found - маскуйте існування ресурсу за відсутності прав.
409 Conflict - конфлікт версій/станів (optimistic lock, ідемпотентність).
451 Unavailable For Legal Reasons - блок по комплаєнсу/юрисдикції.
Ліміти та захист
408 Request Timeout - клієнт занадто повільно відправляє тіло.
409/425 Too Early - заборона раннього повтору в 0-RTT/TLS 1. 3.
429 Too Many Requests - з «Retry-After» і політикою ліміту.
499 Client Closed Request - (на периметрі/NGINX) клієнт розірвав з'єднання.
Дані та бізнес-правила
422 Unprocessable Content - бізнес-валідація пройшла схему, але сенс невірний.
423 Locked - ресурс заблокований (KYC review, AML freeze).
409 Conflict - подвійна відправка, гонка, ліміт стану (наприклад, «вже в обробці»).
410 Gone - ендпоінт/ресурс видалений (депрекейт завершений).
Серверні
500 Internal Server Error - невідома помилка; не розкривати деталі.
502 Bad Gateway - залежність повернула помилку/проксирування.
503 Service Unavailable - деградація/планові роботи; додати'Retry-After'.
504 Gateway Timeout - таймаут залежностей.
5) Семантика ретраїв та ідемпотентності
Не можна ретраїти: 400/ 401/403/404/422 (якщо клієнт не змінив запит).
Можна ретраїти: 408/429/5xx/425/499/504 (с backoff + jitter).
Ідемпотентність: для'POST'включайте'Idempotency-Key'( UUIDv4).
На конфлікт повторного виконання повертайте 409 з'hint: "Use same Idempotency-Key or GET status"`.
Додавайте'Idempotency-Replay: true'при поверненні збереженого результату.
HTTP/1.1 429 Too Many Requests
Retry-After: 3
RateLimit-Limit: 50
RateLimit-Remaining: 0
RateLimit-Reset: 1730641030
6) Валідація входу: структура помилок полів
Для 400/422 використовуйте масив помилок полів:json
{
"type": "https://errors.example.com/validation",
"title": "Validation failed",
"status": 422,
"code": "VALIDATION_ERROR",
"trace_id": "01HX4...XYZ",
"errors": [
{"field": "amount", "rule": "min", "message": "Must be >= 10"},
{"field": "currency", "rule": "enum", "message": "Unsupported currency"}
]
}
7) Часткові невдачі (batch/partial failure)
У батч-ендпоінтах не ховайте помилки всередині 200 без структури. Повертайте 207 Multi-Status або 200 c масивом результатів, де кожне завдання має свій статус:json
{
"status": "partial",
"succeeded": 8,
"failed": 2,
"results": [
{"id": "op1", "status": 201},
{"id": "op2", "status": 422, "error": {"code":"VALIDATION_ERROR","detail":"..."}}
]
}
8) Пагінація і «порожні» відповіді
Порожня колекція - 200 с'items: []', не 404.
Кінець сторінки -'next _ page _ token'відсутній.
Некоректний токен - 400 с'code: PAGINATION_CURSOR_INVALID`.
9) Webhooks: Надійна доставка
Підписуйте події (HMAC) і перевіряйте до обробки.
Відповідь на успішну обробку - 2xx (краще 204).
Тимчасові збої одержувача - 5xx; відправник повторює (експоненціальний backoff, джиттер).
Дедуплікація по'event _ id'і збереження результату (idempotent consumer).
Неприпустимий payload - 400/422 без повторів.
10) Відповідності протоколам (gRPC/GraphQL)
gRPC → HTTP:- `INVALID_ARGUMENT` → 400
- `UNAUTHENTICATED` → 401
- `PERMISSION_DENIED` → 403
- `NOT_FOUND` → 404
- `ALREADY_EXISTS` → 409
- `FAILED_PRECONDITION` → 412/422
- `RESOURCE_EXHAUSTED` → 429
- `ABORTED` → 409
- `UNAVAILABLE` → 503
- `DEADLINE_EXCEEDED` → 504
json
{
"data": { "createPayment": null },
"errors": [{
"message": "Forbidden",
"extensions": { "code": "FORBIDDEN", "status": 403, "trace_id": "..." },
"path": ["createPayment"]
}]
}
Рекомендується для критичних помилок використовувати не 200, а відповідний HTTP-код.
11) Заголовки та підказки клієнту
«Retry-After» - секунди/НТТР-дата (429/503/425/408).
«Warning» - м'які деградації або депрекейт («199 - Feature X is deprecated»).
`Deprecation`, `Sunset`, `Link: <...>; rel = «deprecation»'- для керованого відключення.
«Problem-Type» (кастомний) - швидкий роутинг помилок на клієнті.
'X-Trace-Id '/' Correlation-Id'- зв'язує логи/трейси.
12) Безпека повідомлень
Не повторюйте вхідні секрети (токени/підписи) в тілі відповіді.
Маскуйте PAN/PII ('1234').
Для 401/403 - не розкривайте, який саме атрибут провалився.
Для 404 замість «resource exists but not yours» - просто 404.
13) Спостережуваність помилок
Метрики:- `http_errors_total{status, route, tenant}`
- 'error _ classes _ total {code}'( по'code'з тіла)
- частка 429, 5xx;'p95 '/' p99'latency для помилкових відповідей окремо
- 'retry _ after _ seconds _ bucket'- гістограма порад по повторах
- зв'язуйте відповідь з «trace _ id», зберігайте «code», «type», «status», «route», «tenant», без PII.
- сплеск'5xx _ rate> X%'при RPS> N;
- зростання 429 на критичних маршрутах;
- 'timeout/504'у залежностей;
- часті 409/ідемпотентність → ознака гонок.
14) Приклади
14. 1 422 (бізнес-валідація)
json
{
"type": "https://errors.example.com/payments/limit-exceeded",
"title": "Limit exceeded",
"status": 422,
"code": "PAYMENT_LIMIT_EXCEEDED",
"detail": "Daily withdrawal limit reached for KYC1.",
"hint": "Increase limits after KYC2 or try tomorrow.",
"trace_id": "01J5...XYZ"
}
14. 2 409 (ідемпотентність)
HTTP/1.1 409 Conflict
Idempotency-Replay: true
json
{
"type": "https://errors.example.com/idempotency/replay",
"title": "Duplicate request",
"status": 409,
"code": "IDEMPOTENT_REPLAY",
"detail": "A request with the same Idempotency-Key was already processed.",
"hint": "Reuse the same Idempotency-Key and GET the operation status."
}
14. 3 429 (ліміти)
json
{
"type":"https://errors.example.com/rate/too-many-requests",
"title":"Too many requests",
"status":429,
"code":"RATE_LIMITED",
"detail":"Per-key rate limit exceeded.",
"hint":"Retry after the time specified in Retry-After header."
}
15) Антипатерни
Повертати 200 з текстом помилки в тілі.
Змішувати різні формати помилок між сервісами.
Розкривати стек/SQL/імена таблиць/внутрішні URL в'detail'.
Використовувати «message» замість стабільного «code »/« type».
Повертати 500 при очікуваній бізнес-помилці (наприклад, «баланс недостатній»).
Непослідовна семантика між REST/GraphQL/gRPC.
16) Специфіка iGaming/фінансів
Чіткі коди для KYC/AML/санкцій: `KYC_REQUIRED`, `KYC_REVIEW`, `AML_LOCK`, `SANCTION_BLOCKED`.
Обмеження юрисдикцій: 451 з безпечним формулюванням без зазначення списків.
Грошові write-операції: 409/423 при конкуренції і блокуваннях,'hint'з вікном повтору.
Інваріанти лімітів гравця: використовуйте 422 для порушень правил відповідальних платежів.
Аудит: незмінювані журнали рішень (код, час, актор, trace_id).
17) Чек-лист prod-готовності
- Єдина JSON-схема помилок, стабільні'type '/' code'.
- Маппінг HTTP ↔ gRPC/GraphQL узгоджений і задокументований.
- Семантика ретраїв +'Retry-After'; ідемпотентність для write.
- Маскування PII/секретів; 404 для приховування ресурсів.
- Метрики помилок і алерти; кореляція з'trace _ id'.
- Політики депрекейту: `Deprecation`, `Sunset`, `Link`.
- Тести: negative/fuzz, конфлікт версій, падіння залежностей, double-submit.
- Гайд клієнтам: приклади бек-оффів і обробка 409/422/429/5xx.
18) TL; DR
Стандартизуйте єдиний JSON-формат помилок c'type '/' code '/' trace _ id', використовуйте коректні HTTP-коди, розрізняйте валідацію (400/422), права (401/403/404), конфлікти/ідемпотентність (409) і ліміти (429). Давайте чіткі'Retry-After'і'hint', маскуйте чутливі дані, логуйте помилки з'trace _ id'і будуйте алерти по 5xx/429/p99.