Skip to main content

Troubleshooting: template variant missing (NO_VARIANT_FOR_CHANNEL)

A render or send against a reusable content template failed with HTTP 422 and the error code NO_VARIANT_FOR_CHANNEL. That means the template you referenced has no variant authored for the channel you asked for, and walking the template’s fallback_chain found no other channel variant to substitute. The request is valid — the template simply does not cover that channel. This page shows how variant resolution works and where the missing variant belongs.

What template variants are

A reusable content template (POST /messages/content-templates) is a single template entry that carries one variant per channel — the per-channel renderer bound to that template. Each variant holds the channel-rendered copy:
  • body for SMS and other text channels,
  • media_urls for MMS,
  • components for WhatsApp structured sends,
  • subject plus body_html / body_text for email,
  • suggestions for RCS.
Variants live on the template’s variants map, keyed by channel name, so one template entry can serve many channels. You author variants through the content-templates endpoints (POST / PATCH /messages/content-templates) and preview any channel with POST /messages/content-templates/:id/render.

How fallback_chain resolution walks channels

When the requested channel has no variant, the resolver walks the template’s fallback_chain — an ordered list of channel names — and uses the first variant it finds:
  1. If the target channel has a variant, the resolver returns it as a primary resolution.
  2. Otherwise it walks fallback_chain in order, skips the target channel itself, and returns the first variant that exists as a fallback resolution.
  3. If the chain exhausts with no variant anywhere, the resolver returns null — and the caller raises the 422.
The walk is deterministic: position in fallback_chain decides priority, and a chain entry that names a channel with no authored variant is simply skipped over.

The 422 result contract

When resolution returns null, the API responds with HTTP 422:
The requested_channel on a successful render is always paired with a rendered_channel field, which tells you whether the response used the primary channel or a fallback hop. When every hop misses, there is no rendered_channel — resolution returned null and the request fails closed instead of guessing a channel.

Fix: add a variant for the channel

Add the missing variant to the template, or extend its fallback_chain:
  1. Fetch the template (GET /messages/content-templates/:id) and look at its variants map — find which channel keys exist.
  2. Author the variant for the channel you need — for example a WhatsApp variant with the right components, or an email variant with subject and body_html — via PATCH /messages/content-templates/:id.
  3. If you intended the send to substitute another channel when yours is missing, add that channel to fallback_chain and make sure a variant for it actually exists on the template.
  4. Re-run POST /messages/content-templates/:id/render with target_channel set to the failing channel; it returns the variant and reports resolution: "primary" or "fallback" so you can confirm the resolution path before you send.

Where the mistake usually lives

The chain and the variant are authored in two different places, and most NO_VARIANT_FOR_CHANNEL errors are a mismatch between the two:
  • A fallback chain entry with no authored variant. The template names whatsapp in fallback_chain, but nobody authored the WhatsApp variant — the hop is skipped, and if nothing else resolves you get the 422.
  • The default channel covers less than you think. Templates have a default_channel, and renders without an explicit target_channel use it. If you only authored the SMS variant but the default channel points at WhatsApp, every unparameterised render fails.
  • A flow-builder step references a template that doesn’t cover the flow’s channel. In the multi-channel fallback flow builder, a step that renders a template against rcs or whatsapp only succeeds when that template carries a variant for the channel the step targets.
In each case the fix is the same: make the variant set match what the fallback chain and the send path actually ask for.

See also