API errors and validation
Look up what a failed Studio API call means, from its status code, its error_code or its message. It also lists the layout rules the API checks every time it writes a composition. For the routes themselves, see API endpoints.
The example on this page is the Blog post template, whose entry UID is blt4444444444444444, in the project 0123456789abcdef01234567.
What an error looks like
Most errors come back in this shape.
{
"error_message": "The composition is invalid",
"error_code": 32,
"errors": {
"ui.slots.main[0].uid": ["Duplicate node uid \"hero\" — every node uid must be unique within the tree."]
}
}
| Field | What it holds |
|---|---|
error_message | A summary for people. The text can change, so don't match on it. |
error_code | A number that stays the same. Branch on this. |
errors | Only there when a field or node is at fault. Each key is the path of that field or node, and each value is a list of messages. |
Two kinds of failure come back in a different shape.
| Status | When | Shape |
|---|---|---|
400 | The body or query has a missing field, a wrong type or a value that isn't allowed, such as a missing title or a limit of 200. The route never runs. | { "statusCode": 400, "message": ["…"], "error": "Bad Request" }, with the first problem for each field |
408 | The call ran longer than 60 seconds. | { "statusCode": 408, "message": "Request Timeout" } |
What each status code and error code means
Many different problems share 422, so use the error_code to tell them apart.
| Status | error_code | error_message | What's wrong |
|---|---|---|---|
401 | 39 | Missing Contentstack credentials — the request must include an authtoken or an OAuth access token (authorization: Bearer <token>) to create or read compositions | A composition call reached the API with neither an authtoken nor an authorization: Bearer token. The API checks this after it finds the project and checks your stack access, so another error can come first. |
401 | 48 | Missing Contentstack credentials — the request must include an authtoken or an OAuth access token (authorization: Bearer <token>) to list registered components | The same, on the registered components route. |
404 | 1 | Project not found | No project with that UID in this organization, or you can't read it. errors is { "uid": ["is not valid."] }. Composition and registered component routes return this too when the project in the path doesn't exist. |
404 | 30 | Composition not found | No composition with that UID in the project's stack. errors is { "uid": ["is not valid."] }. |
409 | 33 | A composition with this composable_uid already exists | Another composition in the content type already has this composable_uid, in any language. errors is { "composable_uid": ["is not unique."] }. |
409 | 47 | A composition with this … already exists | Another composition already has a value that must be unique, such as the same title. errors names the field. |
409 | 41 | This composition is referenced by other compositions and cannot be deleted | You deleted a composition that another composition uses. errors.references lists them. See Why deleting a composition fails with 409. |
422 | 21 | Stack not found | Your token has no access to the project's connected stack: read access for a GET, write access for anything else. The API gets only a yes or no from the access check, so it can't tell a stack you can't access from one that doesn't exist. Reading a project you can't access answers 404 with code 1 instead. |
422 | 32 | The composition is invalid | The layout breaks a rule. errors lists every problem at once. See Layout rules the API checks on every write. The API also returns it when you send a blank composable_uid. |
422 | 40 | composable_uid is immutable and cannot be changed on update | A PUT sent a composable_uid different from the stored one. |
422 | 37 | Failed to decompress the composition UI spec | You asked for decompression=true, but the stored ui can't be read. errors is { "ui": ["is not valid."] }. |
422 | 2 on create, 3 on change, 4 on delete | An Error occurred while creating the project, and the same for updating or deleting | The API couldn't save the project. When a value must be unique, such as a second live project with the same stack and content type, errors names the field with is not unique. |
500 | 36 | Failed to compress the composition UI spec | The API couldn't compress ui and read it back unchanged, so it saved nothing. |
The status Contentstack returned, or 502 | 38 | Contentstack rejected the composition: … | Contentstack refused a composition read or write. A 401, 403, 409, 422 or 429 from Contentstack keeps its status. A server error or no answer becomes 502. |
The status the component service returned, or 502 | 46 | The component service rejected the request: … | The registered components service refused the call. A 403 or other client error keeps its status. |
502 | 46 | The component service could not be reached (…). | The registered components service didn't answer, or answered with something other than a list. |
A 401 or 403 passed on from Contentstack means Contentstack refused your token. Fix the token or its permissions instead of retrying. A 502 is safe to retry.
Layout rules the API checks on every write
The API checks the layout every time it writes a composition. On a PUT, it merges your changes first and then checks the whole composition. So if the stored layout breaks a rule, a PUT to any field fails, even a PUT to title.
Every problem comes back in one answer. An error stops the write with 422 and error code 32. A warning doesn't stop it. Warnings come back in a warnings array on the 201 or 200 answer. Each warning has a path, a message and an errorKey that names the rule.
Paths start at ui and follow slots and positions, such as ui.slots.main[0]. In the tables, "…" stands for the node UID, type or slot name the message names.
Errors that stop the write
| Message | What's wrong |
|---|---|
| The root node of a composition must have type "page", got "…". | The root node isn't a page node. Templates and sections both start with page. section is only for containers inside the tree. |
| Node at … is not a valid object. | Something in a slot isn't a node. |
| Node at … must have both a "uid" and a "type". | A node is missing its uid or its type. |
| Duplicate node uid "…" — every node uid must be unique within the tree. | Two nodes have the same uid. |
| Node "…" declares a binding at the node level. Bindings must live at props.<name>.binding. | A node has binding or bindings on itself. Put the binding on the prop. |
| Node "…" uses a flat "children" array. Children must live in slots.<slot-uid>. | A node has a children array. Put child nodes in a slot. |
| Node "…" has a top-level "condition"/"mode". Discriminators must live in metadata. | condition or mode is on the node. Put it in metadata. |
| Placed section node "…" must set metadata.compositionUID. | A section-composition node doesn't say which section it places. |
| Slot "…" on node "…" must be an array. | A slot holds something other than a list of nodes. |
| Slot "…" on node "…" is an empty array. Empty slots crash the renderer — populate it or remove the slot key. | A slot is []. Put a node in it, or remove the slot. |
| Repeater "…" has no props.items.binding — it will iterate nothing. | A Repeater doesn't say what to repeat. |
| A section composition must have an empty connected_content_type. | A section has a connected_content_type. Only a connected template has one. |
Warnings that don't stop the write
| Message | What it means |
|---|---|
| Node type "…" is not a built-in. It must be registered via registerComponent, or it renders as "unknown component". | The API doesn't know this type. That's expected for your own components. Check the name matches a registered component. |
| Repeater "…" has no metadata.mode "preview" — the canvas will show a single placeholder (production still iterates). | The canvas shows one placeholder for this Repeater. Your live site still repeats it. |
| Repeater "…" has no Condition Block child. If it iterates a reference or modular-block field, items won't render without one. | A Repeater over a reference or modular blocks field renders without one. A Condition Block adds a branch for each type. Over any other field, ignore this. |
| This composition places section-composition nodes; ensure linked_sections references every placed section, or the editor shows "Template Did Not Load". | The API shows this whenever the layout places a section. It fills in linked_sections from ui itself, so there's nothing to change. The message names an editor card that no released editor shows. |
The API checks the shape of the layout only. It doesn't load your components or your content type, so it doesn't check that a binding points at a real field or that a URL pattern is valid. For URL patterns, see URL variables.
Why deleting a composition fails with 409
A DELETE fails with 409 and error code 41 when another composition uses the one you're deleting, in any language. For the Blog post template, errors looks like this.
{
"references": ["Referenced by 1 composition(s): bltaaaaaaaaaaaaaaaa. Pass ?force=true to delete anyway."]
}
GET /v1/projects/0123456789abcdef01234567/compositions/blt4444444444444444/references lists the same entries with their titles. Add force=true to the DELETE to delete it anyway.
If the API can't finish checking what uses the composition, it doesn't delete it. The call fails with error code 38 and the status Contentstack returned.
Errors from the registered components route
| Status | error_code | What's wrong |
|---|---|---|
401 | 48 | Neither an authtoken nor an authorization: Bearer token reached the API. |
404 | 1 | No project with that UID in this organization. A deleted project answers the same way. |
422 | 21 | The access check on the project's connected stack failed. Your token needs read access to it. |
| The component service's status | 46 | The component service refused the call, such as with 403. |
502 | 46 | The component service didn't answer, failed, or sent something other than a list. |
An empty list isn't an error. A project with nothing synced answers 200 with { "registered_components": [], "meta": { "count": 0 } }. If you registered a component in your app's code but never synced it with csdx studio:component:sync, it isn't in the list. See CLI commands.
Where to go next
- Look up the routes, headers and parameters: API endpoints.