Errors
There is no single error envelope across the platform. Four shapes exist today, depending on where the error is raised. Clients should key on the HTTP status first and treat the body as best effort.
1. Authentication and role errors
Raised by the security layer. Both bodies are JSON.
| Status | Body |
|---|---|
| 401 | {"error": "UNAUTHORIZED", "message": "Authentication is required"} with WWW-Authenticate: Bearer and Cache-Control: no-store. |
| 403 | {"error": "FORBIDDEN", "message": "...", "requiredRoles": ["..."]}. requiredRoles is present when the endpoint names roles. |
A request that fails token verification in the OIDC layer itself may return 401 with an empty body. The body of that case was not checked.
2. Client errors with a message
The LMS, mentorship, onboarding and program services turn any 4xx WebApplicationException that has no body into:
{ "error": "Conflict", "message": "Cannot cancel a completed session", "status": 409 }
error is the HTTP reason phrase, message is the sentence written at the throw site, and status repeats the code. A response that already carries a body is left alone. Server errors (5xx) are not mapped. They keep Quarkus's default opaque error and a correlation id in the server log.
The mentorship service also maps unreadable request bodies to a 400 in the same shape.
3. Rate limit responses
The LMS service returns 429 with {"error": "Rate limited. Max 3 quiz attempts per hour."} or {"error": "Upload rate limit exceeded. Max 10 per hour."}. There is no Retry-After header. This shape has no message or status.
4. Identity provider failures
The identity service returns 502 with {"error": "IDENTITY_PROVIDER_UNAVAILABLE", "message": "The identity provider could not complete this request"} when a Keycloak admin call fails.
Frontend routes
The routes under /api/auth and /api/ai return {"error": "<sentence>"} with the status. The sign-in route uses 400 for a bad request, 401 for a refused password, 502 for an unreachable provider and 503 for a deployment that cannot sign anyone in. /api/status returns 503 {"error": "Status is temporarily unavailable."} if it cannot build a summary.
Not verified
- The body of a bean-validation failure (
@Valid) in each service. - The analytics, AI Copilot and notification services have no client-error mapper in the code. Their 4xx bodies may be empty.
Sources: libs/common-models/.../RoleEnforcementFilter.java, services/{lms,mentorship,onboarding,program}-service/.../ClientErrorBodyMapper.java, services/mentorship-service/.../JsonBindErrorMapper.java, services/identity-service/.../IdentityProviderFailureMapper.java, services/lms-service/.../{QuizResource,AssignmentResource}.java, frontend/src/app/api/**.