Studio Docs

Studio API: Errors and Validation

How the managed Studio API service reports failures, the full status + error_code catalog, and the Studio rules it enforces on every write. For the routes themselves, see the Endpoint reference.

Do It With a Skill

use-compositions-api writes calls that satisfy this validation layer, which compresses the ui node tree and rejects malformed layouts.

curl -fsSL https://studio-documentation.contentstackapps.com/install.sh | sh

The Error Envelope

Every service-level error uses one shape:

{
  "error_message": "<human-readable summary>",
  "error_code": 32,                 // stable numeric code (table below)
  "errors": {                       // present when there are field-level issues
    "<path>": ["<message>", …]      // keyed by the offending field / node path
  }
}

Two things make this actionable:

  • error_code is stable: branch on it, not on the message text.
  • errors is keyed by path: for a bad layout, each failing node path appears with its own message, and all failures come back at once (you don’t fix-and-resubmit one at a time).

One exception: 400s. DTO-shape failures (missing title, a non-object ui, a bad enum) are caught by the framework’s request validator before the handler, so they use the framework’s default shape ({ statusCode, message: [...], error: "Bad Request" }), not the envelope above. Everything else uses the envelope.

Status + error_code catalog

HTTPerror_codeWhenerrors payload
400- (request validator)DTO shape/type/enum failure (missing title, bad place_composition_as, ui not an object, limit out of range)framework default shape
40139 composition_missing_auth_tokenNeither a session authtoken nor an OAuth Authorization: Bearer token reached the service. Not a rejection of OAuth, both styles are accepted-
4041 project_not_foundProject not found (access-check fetch){ "uid": ["is not valid."] }
40431 composition_project_not_foundProject vanished between checks (rare race){ "projectUid": ["is not valid."] }
40430 composition_not_foundEntry uid not found{ "uid": ["is not valid."] }
40444 composition_flavor_mismatch…/{uid}/template on a section (or /section on a template){ "uid": ["This composition is a \"section\", not a \"page\"."] }
42221 stack_not_foundAccess denied: no read/write on the project’s stack (Studio convention, not 403)-
42232 composition_invalidStructural validation failed{ "<node path>": ["<rule message>", …] }, one entry per failing path, all at once
42232 composition_invalidProvided composable_uid was blank/whitespace-only{ "composable_uid": ["composable_uid must not be blank when provided."] }
42240 composition_uid_immutablePUT tried to change composable_uid{ "composable_uid": ["composable_uid is immutable and cannot be changed."] }
42237 composition_decompression_failedGET …?decompression=true but the stored ui is corrupt{ "ui": ["is not valid."] }
40933 composition_uid_conflictcomposable_uid already used, checked content-type-wide (all locales). errors is keyed by the field the CMA flagged (usually composable_uid).{ "composable_uid": ["is not unique."] }
40941 composition_referencedDELETE without force while other compositions reference this one{ "references": ["Referenced by N composition(s): …"] }
50036 composition_compression_failedui round-trip integrity check failed before persist-
401 / 403 / 409 / 422 / 429 / 50238 composition_cma_errorContentstack CMA failure: any upstream 4xx status is preserved. Only genuine 5xx/gateway failures become a 502.upstream message in error_message

Two codes worth internalizing. 422 stack_not_found means access denied, not “missing”. Check the caller’s rights on the project’s stack. And a 409 on create almost always means a duplicate composable_uid somewhere in the content type (including another locale), caught before the write.

Codes on the other route groups

The table above covers the composition routes. The rest of the service uses the same envelope with its own codes:

HTTPerror_codeWhereWhen
4041 project_not_foundProjectsThe uid isn’t a live project in your organization (also returned when you lack access, rather than confirming it exists)
42221 stack_not_foundProjectsAccess denied on the project’s connected stack, on create, update, and delete alike
4222 project_create_failedProjectsAn authorization-SDK failure during create
4223 project_update_failedProjectsAn authorization-SDK failure during update
4224 project_delete_failedProjectsAn authorization-SDK failure during delete
40148 registered_components_missing_auth_tokenRegistered componentsNeither credential reached the service. The counterpart to 39 on the composition routes
42245 registered_components_fetch_failedRegistered componentsAn authorization-SDK failure during the lookup
502 or upstream status46 registered_components_upstream_errorRegistered componentsThe component service was unreachable, failed, or returned an unexpected body. A client error such as 403 passes through with its own status

Why so many 422s. The envelope defaults to 422 whenever a code is raised without an explicit status. That’s why access denial, project write failures, and validation failures all land on the same status. The error_code is what distinguishes them, which is the reason to branch on it rather than on the status.

What the Service Validates

On every write (create, and any update that includes ui), the service checks the layout against Studio’s structural rules and returns results in two tiers:

TierEffectWhere it shows
ErrorsBlock the write422 composition_invalid, keyed by path
WarningsDon’t blockwarnings[] on the 201 / 200 success body

The checks are crash-guards, the shapes that would break the canvas or the renderer if they slipped through. Representative rules:

  • Root must be page: for both templates and sections. section is a nested-container type, never the tree root.
  • No empty slots: an empty slots array crashes the renderer. Populate it or drop the key.
  • Repeater / Condition Block shape: a Repeater needs its iteration metadata. A Condition Block needs its cases. (See Smart containers.)
  • Placement consistency: a section can’t carry a url / connected_content_type. Those are stripped or rejected per flavor.
  • Section placement warning: a tree that places section-composition nodes warns you to keep linked_sections in sync, or the editor shows “Template Did Not Load”.

Because the results are collected, a single POST tells you every structural problem at once. Fix them together and resubmit.

Why these and not more? The service validates the structural problems it can catch without loading your components or content-type schema, the ones that hard-break rendering. Deeper semantic checks (URL-variable correctness, binding-context matching) depend on the connected content type and are enforced by the canvas/runtime, not this pre-check. The layout-tree vocabulary these rules police is documented once in Building Blocks.

Handling Errors Well

  • Branch on error_code, not error_message: messages are translated and may change. Codes are stable.
  • On 422 composition_invalid, read every key in errors: they’re all there. Don’t fix one and resubmit blind.
  • Treat 422 stack_not_found as an auth problem, not a missing resource.
  • On 409 at create, pick a different composable_uid (or omit it to let the service backfill a unique one).
  • On 409 composition_referenced at delete, call GET …/{uid}/references to see who depends on it before deciding whether to ?force=true.
  • Retry 502 (transient gateway). A preserved 401/403 is not retryable. Fix the credential/permission.

See also: Endpoint reference · Compositions · Projects · Chapter overview · Building Blocks.