5.2 KiB
title, tags
| title | tags | |||
|---|---|---|---|---|
| Error Codes |
|
Error Codes
This page documents the error shape returned by the AMN backend and the HTTP status mapping used across services.
Standard error shape
The canonical helper is ResponseHandler.error in backend/src/shared/utils/response-handler.ts. Modern routes return:
{
"success": false,
"message": "Validation failed",
"error": "Validation Error",
"statusCode": 400,
"timestamp": "2026-05-23T10:00:00.000Z",
"path": "/api/auth/register",
"method": "POST",
"data": [ /* field-level details for validation errors */ ]
}
Uncaught errors are formatted by shared/middleware/errorHandler.ts:
{
"success": false,
"error": "Internal Server Error",
"statusCode": 500,
"stack": "..." // only in NODE_ENV=development
}
Legacy routes (chiefly /api/users legacy paths, /api/marketplace legacy paths, /api/payment/decentralized/*, parts of /api/payment/shkeeper/*) return ad-hoc shapes such as { "error": "..." } or { "success": false, "message": "..." }. Treat any non-2xx response as an error and read whichever of error / message is present.
HTTP status mapping
| Status | When | Examples |
|---|---|---|
200 OK |
Successful read or mutation | Most GETs, idempotent PUTs/PATCHs |
201 Created |
Resource created | POST /api/marketplace/purchase-requests, POST /api/auth/register (when user created), POST /api/marketplace/reviews |
202 Accepted |
Async accepted (provider webhooks) | SHKeeper webhook acknowledgement |
204 No Content |
Mutations with no body to return | Rare — most endpoints return the updated object |
400 Bad Request |
Validation failure, malformed input | express-validator errors, bad MongoIds, missing fields |
401 Unauthorized |
Missing or invalid JWT | Access token required, Invalid or expired token |
403 Forbidden |
Authenticated but not allowed | Role check failed, email not verified, ownership check failed |
404 Not Found |
Resource (or route) missing | notFoundHandler, Resource not found from ResponseHandler.notFound |
409 Conflict |
Duplicate / state collision | USER_EXISTS, duplicate review, dispute already open |
423 Locked |
Account temporarily locked | After repeated failed logins (Redis-tracked) |
429 Too Many Requests |
Rate limit hit | Currently issued only by per-feature Redis limits (auth / AI); global limiter is disabled |
500 Internal Server Error |
Unhandled exception | Caught by errorHandler; included stack trace in dev |
502 Bad Gateway |
Upstream provider failure | OpenAI / SHKeeper unreachable |
Application error codes
The error field of the response envelope contains a human-readable category. Currently used codes:
| Code | Meaning | Returned by |
|---|---|---|
Validation Error |
express-validator rejected the body |
All *Validation middlewares |
Not Found |
Generic resource lookup miss | ResponseHandler.notFound |
Unauthorized |
No token / bad token | authenticateToken, ResponseHandler.unauthorized |
Forbidden |
Role/ownership check failed | authorizeRoles, ResponseHandler.forbidden |
Internal Server Error |
Catch-all 500 | ResponseHandler.internalError, errorHandler |
USER_EXISTS |
Email already registered | POST /api/auth/register |
For auth-specific 4xx responses the body's message carries the user-facing text (often Persian/Farsi for legacy reasons):
"کاربری با این ایمیل قبلاً ثبتنام کرده است"- email already in use (409)"کد تحویل نادرست است"- wrong delivery code (400)"شما مجاز به ایجاد کد تحویل برای این درخواست نیستید"- not the buyer (403)
Mongoose-specific mappings
Handled in errorHandler:
| Mongoose error | Mapped status | Body message |
|---|---|---|
ValidationError |
400 | Validation Error |
MongoError code 11000 |
409 | Duplicate resource |
JsonWebTokenError |
401 | Invalid token |
TokenExpiredError |
401 | Token expired |
Webhook-specific
| Provider | Endpoint | Status on success | Status on signature mismatch |
|---|---|---|---|
| SHKeeper pay-in | POST /api/payment/shkeeper/webhook |
200 { success: true } |
401 { success: false } (then ignored) |
| SHKeeper payout | POST /api/payment/shkeeper/payout/webhook |
200 / 400 with { success, message, data } |
400 |
| Generic payment callback | POST /api/payment/callback |
200 { success: true, message } |
400 |
If a webhook is acknowledged with non-2xx, the provider re-delivers (SHKeeper retries every 60 seconds).
Client guidance
- Always parse
response.json()even on non-2xx — bothmessageanderrorare useful for UX surface text. - For optimistic UI, treat
409as "your action raced — refresh". - For
401, attempt one silent refresh-token call before redirecting to sign-in. - For
403, do not retry — the user lacks permission. - For
423, surface the lockout window from themessage/dataand direct the user to password reset.