What this is about
When a request goes wrong, CDMS answers with an error status (400 and above) and an error response. The error response is a small JSON object without data. It tells you what happened, in a form a program can evaluate.
An error response, labeled
POST /api/rest/hr/employee/read/5a2b…
{ "response": ["name"] }{
"error": "MissingPermissionException", ← kind of error
"messageKey": "missing-permission|hr-employee-read",
└── key ──────┘ └── detail ──┘ ← branch on this
"code": "403", ← status as text
"layer": "validation" ← layer, for debugging only
}| Field | always present? | Meaning | in the client |
|---|---|---|---|
error | yes | name of the error class, e.g. ApiValidationException | write it to the log |
messageKey | yes | fixed key, often with details after | | branch on this |
message | only in the server’s debug mode | technical text of the cause | never show it, never evaluate it |
code | yes | the HTTP status as text, e.g. "422" | same as the status of the response |
layer | yes | layer in which the error occurred | for debugging only |
violations | only for validation errors | all violated field rules with path | map them to the form fields |
id | only in the LENIENT special case | id of the created object | keep it, read later |
stacktrace | only in the server’s debug mode | technical call stack | never evaluate it |
The messageKey in detail
The messageKey has the form key|detail|detail. Before the first | is the key, i.e. the kind of problem. After it, depending on the error, come details such as a field name, a role name, an id or a value.
messageKey | key | details |
|---|---|---|
missing-permission|hr-employee-read | missing-permission | the missing role |
invalid-value|data.amount | invalid-value | path of the field in the body |
wrong-value-in-where|amount|abc | wrong-value-in-where | field and value of the filter |
recursive-create-not-allowed|employees | recursive-create-not-allowed | the relation |
not-found | not-found | none |
validation-failed | validation-failed | none, the details are in violations |
CDMS_OPTIMISTIC_LOCK_CONFLICT | CDMS_OPTIMISTIC_LOCK_CONFLICT | none |
This is how you get the key in the client:
const [key, ...details] = body.messageKey.split('|');
// key = "missing-permission"
// details = ["hr-employee-read"]switch (key) {
case 'missing-permission': … // role missing
case 'not-found': … // object gone or invisible
case 'validation-failed': … // violations into the form
default: … // general message
}Keys from the persistence layer are written in capitals (CDMS_TENANT_REQUIRED), the others in lowercase with hyphens (missing-id). For the client this does not matter: compare the key character by character, exactly as it is.
Three shapes of error bodies
Not every refusal comes from CDMS itself. The CIAS filter chain runs first and answers in its own shape, and some refusals have no body at all.
messageKeyerror,messageKey,code,layer- every refusal from the role check on
- also 400 for a request that cannot be read
error, without messageKey{"error": "cias.authentication.tenant-unresolved", "message": "request refused"}- the tenant cannot be determined, is not served or is missing
- here the key is in the field
error
- 401: token invalid or expired, header
WWW-Authenticate - 403: no token at all
- 404: the path does not exist
More about the refusals of the filter chain in Access without a token and 401, 403 or 404?.
Validation: violations
Only a validation error has a list violations. Each entry names the field with its path and the violated rule:
POST /api/rest/crm/customer/create
{ "data": { "name": "" }, "response": ["id"] }{
"error": "ApiValidationException",
"messageKey": "validation-failed",
"code": "422",
"layer": "API",
"violations": [
{ "field": "name", "rule": "cannot-be-empty" },
{ "field": "email", "rule": "cannot-be-null" }
]
}How to map the entries to the form fields is described in Getting validation errors into the form. Which rules exist: Validation.
The special case: 200 in error shape
After a create in LENIENT mode, creating can succeed while only the read-back fails. Then you get status 200, but a body in error shape with the id of the new object:
{
"error": "CreateSucceededReadFailedException",
"messageKey": "CDMS_CREATE_SUCCEEDED_READ_FAILED",
"code": "200",
"layer": "system",
"id": "5a2b…"
}The object exists.
Keep the id and read it later,
do NOT create it again.See Create and read back: STRICT or LENIENT.
How a client evaluates it
-
1ClientStatus 2xx?
-
2Clientyes, and
messageKeyisCDMS_CREATE_SUCCEEDED_READ_FAILED→ created, keep theid -
3Clientyes, otherwise → use
data -
4Clientno → read the body if there is one. Does it have a
messageKey? -
5Clientyes → evaluate the key before the first
|, for 422 alsoviolations -
6Clientno → decide by status: 401 renew the token, 403 with
errorcheck the tenant, otherwise a general message
Pitfalls
Where to go next
- What each status means: Map of status codes
- The three codes that are often mixed up: 401, 403 or 404?
- The envelope of a success response: The response format