Troubleshooting: IVR NLU intent catalogue authoring rejections
The intent catalogue is where you author the named intents and example utterances your conversational IVR classifies against. Writes to it — creating an intent, renaming one, deleting one — can be rejected with four codes that are entirely about your request, not about the model layer. This page decodes each code, the change that clears it, and how these authoring-time rejections differ from the runtime 503s the classifier throws on live calls.Authoring rejections are not runtime 503s
Two different failure families share the NLU surface:- Authoring-time rejections (this page). Returned by the catalogue
endpoints under
/api/v1/voice/ivr-nlu-hintswhen a create, update, or delete cannot be applied: a full catalogue, a name already taken, a stale id. They are deterministic — the same request gets the same rejection until you change it — and the model layer is fine. - Runtime 503s —
IVR_CLASSIFY_LLM_ERRORandIVR_SLOT_EXTRACT_LLM_ERROR— hit live calls when the classifier or slot-extractor leg goes down. The catalogue is fine; the model call is not. Recovery for those (re-prompt, DTMF fallback, queue) lives on IVR NLU classifier and slot-extraction failures.
The catalogue write rejections
IVR_NLU_CATALOGUE_FULL — the 64-intent cap
A tenant catalogue holds at most 64 intents. The cap is checked before
the insert, so a rejected create writes nothing partial. To free a
slot, list the catalogue (GET /api/v1/voice/ivr-nlu-hints) and delete
an intent your flows no longer reference — deletion is the only way to
release a slot, so prefer deleting intents that are truly retired over
ones you might tune later. If you are close to the cap because many
intents cover one menu, consider merging intents whose descriptions
phrase the same middle ground; the
routing guide covers description craft.
IVR_NLU_NAME_CONFLICT — names are unique per tenant
Intent names are lowercase alphanumeric with hyphens or underscores,
max 64 characters, unique per tenant — the same handle rules the
routing guide teaches.
Re-posting a create with a name that already exists returns this 409,
which commonly bites scripts that retry a create after a network
error: the first attempt may have succeeded. List the catalogue, find
the existing intent, and PATCH its description or utterances instead
of posting the create again. A PATCH that keeps an intent’s own name
never conflicts; only renaming onto another intent’s name does.
IVR_NLU_CREATE_ERROR — retry once, then escalate
A 500 on create means the write failed for a non-conflict reason —
transient by design of the code, unlike the deterministic 422 and 409
above. Retry once with an identical body; a create writes nothing
until it succeeds, so the retry cannot double-insert. If the second
attempt also returns IVR_NLU_CREATE_ERROR, escalate with the
request_id the error envelope carries.
IVR_NLU_NOT_FOUND — stale id on update or delete
Ids are stable for the life of an intent, but a delete removes them
outright. A script or dashboard tab holding an id from an earlier
session 404s once another writer deletes that intent. Re-list the
catalogue before batching updates or deletes, and treat
IVR_NLU_NOT_FOUND on a delete as already-done rather than an error.
Malformed payloads return VALIDATION_ERROR
A name with uppercase letters or spaces, an empty utterance, an
utterance over 100 characters, or more than 16 utterances on one
intent does not return a catalogue code — it comes back as the generic
VALIDATION_ERROR 422 with a details map naming the offending
field. Fix the field and resend; the catalogue codes above only return
for well-formed requests the catalogue itself refuses.