The enum the model invented a new value for

A classifier prompt listed the allowed categories. The model occasionally returned a plausible category that was not on the list, and nothing downstream knew what to do with it.

The enum the model invented a new value for

A machine learning engineer at a customer-support platform was debugging tickets that had landed in a queue nobody owned. The feature classified incoming messages into a fixed set of categories, and each category routed to a team. These orphaned tickets had a category label that matched no team, so they routed nowhere and sat.

She pulled the labels. The feature was supposed to output one of a handful of categories. The orphans had a label that was not in that handful. It was not garbage, either. It was a sensible, well-phrased category name that would fit right in if the list had included it. The model had invented a new value that looked exactly like it belonged.

A plausible wrong value is the hard case

Her prompt did the reasonable thing. It listed the allowed categories and told the model to pick one. That works most of the time. On a message that did not sit cleanly in any listed bucket, the model did something very human. It made up a category that described the message well. The invented label was a good description. It was also useless to a system that routes by exact match against a known set.

This is what makes an invented enum value harder than a garbage output. Garbage is easy to catch, because it does not parse or does not look like a category. A plausible new value passes every casual check. It is a string. It reads like a category. It flows through code that never imagined a value outside the list, because the prompt listed the values and the team assumed the list was binding. The list was a suggestion the model mostly honored, and mostly is where the orphaned tickets come from.

Constrain to the set, then validate against it

Listing the options in the prompt is a starting point. It is not enforcement. Enforcement has two layers, and you want both:

  • Constrain generation to the enum. Where the model supports it, use a mode that restricts output to the exact allowed values. If the model can only emit a value from the set, it cannot invent one.
  • Validate every output against the allowed set. After generation, check the returned value is a member of the set. This is a cheap membership test, and it catches anything constrained decoding did not.
  • Treat an out-of-set value as an error, not a category. When a value is not in the set, catch it. Route it to a fallback bucket, retry, or flag it for a human. Do not let it flow downstream as if it were valid.
  • Add a real “other” if you need one. If messages genuinely fall outside your categories, add an explicit bucket for that and a team to own it. Then an unclassifiable message has a home instead of inventing one.

The point is that the allowed set has to be enforced by your code, not entrusted to the model’s willingness to stay inside the lines. A classifier that can only ever emit a known value is a classifier you can build on. One that occasionally emits a plausible new value is a source of tickets that route nowhere, and you will find them by accident, in a queue, weeks later.

How we approach it at Density Labs

In the AI Opportunity Assessment, our fixed two-week, $2,500 engagement that scopes real production work before you build, we look closely at any feature that classifies into a fixed set, because this is a common and quiet failure. We constrain the output to the enum where the model allows it, and we always add a membership check that treats an out-of-set value as an error to catch rather than a label to trust. It is far cheaper to reject an invented category at the boundary than to explain why a customer’s ticket sat unrouted for a week.

The list in your prompt is a wish. The membership check is the enforcement.