> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshoot template composition errors (OVER_LENGTH, INVALID_BUTTON, INVALID_COMPONENT)

> Decode the composition_errors the template preview returns before submit: which component broke which character cap, which button and component shapes Meta and RBM reject, and the exact per-channel limits.

# Troubleshoot template composition errors

Before a template message goes out, Orbit renders the final payload (variables substituted, buttons resolved) and checks every component against the limits Meta (WhatsApp) and Google (RCS) enforce on the wire. Anything a provider would reject comes back in the preview's `composition_errors` array, one entry per problem, each naming the offending `field`, a `code`, and a readable `message`:

```json theme={null}
{
  "field": "components[BODY].text",
  "code": "OVER_LENGTH",
  "message": "BODY text must be 1024 characters or fewer (got 1408)."
}
```

This page decodes the three composition codes:

* `OVER_LENGTH` — a text component exceeds its character cap.
* `INVALID_BUTTON` — a BUTTONS component is empty or over-counted, a label is over-long, or a button type is one Meta does not accept.
* `INVALID_COMPONENT` — a component format or component mix Meta does not define (unknown HEADER format, missing or duplicated BODY).

## Where you see these codes

**In the template dialog.** The create/edit template dialog runs the preview on your draft as you type and surfaces any open composition errors before you submit. While an error is open nothing is submitted: no send is attempted, nothing is queued, and no credit is debited. Fix the field and the error clears on the next preview pass.

**In the messages preview API.** The same `composition_errors` array returns from the preview render endpoint, alongside `unresolved_variables`, `unused_variables`, and an `ok` flag. Automation that assembles template payloads should treat `ok: false` as "do not queue yet" and surface `field` plus `message` to whoever owns the copy.

Caps are measured **after** variable substitution. A 900-character BODY whose `{{1}}` resolves to a 300-character value is over 1,200 characters and fails `OVER_LENGTH` even though the draft text fits. Check the rendered length, not the draft length, when a template only overflows for some recipients.

## OVER\_LENGTH — a component exceeds its cap

The `field` value names the exact component:

| `field` | Cap | Cause and fix |
| - | - | - |
| `body` | WhatsApp 1,024; SMS 1,600; MMS 4,096; email 100,000 | The plain body (or a body-only WhatsApp send) exceeds the channel cap. The `message` names the channel and the rendered length. Trim the copy, or for SMS see [SMS segments and encoding](https://docs.orbit.devotel.io/concepts/sms-segments-and-encoding.md) for how length maps to segments. |
| `components[HEADER].text` | 60 | A text HEADER is one short line. Media headers (IMAGE, VIDEO, DOCUMENT, LOCATION) carry no text cap. |
| `components[BODY].text` | 1,024 | The WhatsApp BODY component, counted after substitution. Move long detail out of the message body. |
| `components[FOOTER].text` | 60 | Footers are a single short line; long disclaimers belong in the BODY or outside the message. |
| `components[BUTTONS].buttons[N].text` | 25 | A button label exceeds 25 characters. Shorten it; the index `N` in `field` tells you which button. |
| `rcs.title` | 200 | The RCS rich-card title. |
| `rcs.description` | 2,000 | The RCS rich-card description. |
| `rcs.suggestions[N].text` | 25 | A suggested-reply or action chip label. |
| `rcs.suggestions[N].postback_data` | 2,048 | The postback payload of a `reply` suggestion. |
| `subject` | 998 | The email subject line (RFC 5322 line-length cap). |

## INVALID\_BUTTON — the BUTTONS component or a button is malformed

| `message` you see | Cause | Fix |
| - | - | - |
| `BUTTONS must contain at least one button.` | A BUTTONS component with an empty `buttons` array. | Add at least one button, or remove the BUTTONS component. |
| `BUTTONS may contain at most 10 buttons (got N).` | More than 10 buttons. | Meta accepts at most 10; drop buttons or fold choices into QUICK\_REPLYs in another template. |
| `Button text must be 25 characters or fewer (got N).` | A label longer than 25 characters. | Shorten the label. |
| `Unsupported button type "…".` | A type outside the accepted set. | Use one of: URL, PHONE\_NUMBER, QUICK\_REPLY, COPY\_CODE, FLOW, VOICE\_CALL, CATALOG, MPM, SPM (OTP on authentication templates). |
| `URL button requires text and url.` | A URL button missing its label or target. | Supply both; the URL may itself carry `{{1}}`-style variables for per-recipient links. |
| `PHONE_NUMBER button requires text and phone_number (E.164).` | A call button missing its label or number, or a non-E.164 number. | Supply the label and an E.164 number such as `+14155550100`. |
| `QUICK_REPLY button requires text.` | A quick reply without a label. | Supply the label (25 characters or fewer). |

## INVALID\_COMPONENT — a component shape Meta does not define

| `message` you see | Cause | Fix |
| - | - | - |
| `HEADER.format must be TEXT/IMAGE/VIDEO/DOCUMENT/LOCATION (got "…").` | A HEADER with an unknown or missing `format`. | Set `format` to one of the five. A typo like `TEXT ` with trailing whitespace or a custom format string is rejected. |
| `Exactly one BODY component is required.` | Zero BODY components, or more than one. | Keep exactly one BODY per template. |
| `At most one HEADER component allowed.` / `At most one FOOTER component allowed.` / `At most one BUTTONS component allowed.` | The same component type appears twice. | Merge the duplicates into a single component. |

## The limits, in one table

| Channel | Component | Limit |
| - | - | - |
| WhatsApp | HEADER (text) | 60 characters |
| WhatsApp | BODY | 1,024 characters |
| WhatsApp | FOOTER | 60 characters |
| WhatsApp | Button label | 25 characters, at most 10 buttons |
| RCS | Title | 200 characters |
| RCS | Description | 2,000 characters |
| RCS | Suggestions | At most 11, 25-character labels, 2,048-character postback data, `https://` media only |
| SMS | Body | 1,600 characters (concatenated) |
| MMS | Body | 4,096 characters |
| Email | Subject | 998 characters |
| Email | Body | 100,000 characters |

All character counts are post-substitution, measured the same way the preview and the send path measure them.

> [!NOTE]
>
> WhatsApp applies caps per component, not to the message as a whole. A template
> can carry 60 + 1,024 + 60 characters across HEADER, BODY, and FOOTER plus button
> labels; no single component may exceed its own cap.

## unresolved\_variables vs unused\_variables

The preview returns two arrays next to `composition_errors` that describe your variable wiring rather than your component shapes:

* **`unresolved_variables`** — placeholders the template references but your `variables` did not cover (`{{1}}`, `{{first_name}}`, `{{contact.custom.tier}}`). The rendered payload keeps the literal token, so the recipient would receive `{{first_name}}` verbatim; the preview therefore keeps `ok` false until every token resolves. Fix: supply the missing key, or fix the token spelling in the template.
* **`unused_variables`** — values you supplied that no placeholder references. This one is advisory and does not block `ok`: nothing breaks, but a key landing here is usually the other half of a typo (you sent `first_name`, the template says `firstname`). Fix: drop the extra key or correct the placeholder it was meant to fill.

Seeing the same name in both arrays across two previews usually means the token and the key drifted in opposite directions; compare them character for character.

## When the provider still rejects

Templates saved before these checks existed, or written straight through the API without a preview pass, can hold over-length or malformed payloads that only surface at delivery time:

* **Meta (WhatsApp)** rejects the send with `error_subcode 2388042` when a stored component exceeds a cap or carries a bad shape. Re-open the template in the dialog to run the preview over the stored payload, trim to the caps above, and resubmit for approval as described in [Create and approve WhatsApp templates](https://docs.orbit.devotel.io/guides/whatsapp/templates-create-approve.md).
* **Google RBM (RCS)** answers `INVALID_ARGUMENT` for the same class of problem on rich cards and suggestions; the RCS limits above and [Build RCS rich cards](https://docs.orbit.devotel.io/guides/rcs-rich-card-builder.md) cover the accepted shapes.

Related failure modes that look like composition errors but are checked elsewhere:

* [WhatsApp template variable count mismatch](https://docs.orbit.devotel.io/troubleshooting/whatsapp-template-var-count.md) — the send-side pre-flight on `template_params` count vs `{{N}}` placeholders, a 400 before the request reaches Meta.
* [Template has no variant for the channel](https://docs.orbit.devotel.io/troubleshooting/template-variant-missing.md) — the routing layer cannot find a variant for the channel you are sending on.
* [SMS segments and encoding](https://docs.orbit.devotel.io/concepts/sms-segments-and-encoding.md) — how GSM-7 vs UCS-2 text maps body length to billed segments.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.