Parsing model output with a regex is a bug waiting for a user

A regex pulled the answer out of the model's text and worked for months. Then a user phrased their input a little differently and the regex quietly missed.

Parsing model output with a regex is a bug waiting for a user

A founder at an analytics startup told me about a bug that had no code change behind it. Nothing had shipped. No dependency had updated. One day a feature that had worked for months started returning empty results for certain users, and the git history showed nobody had touched it.

At the heart of the feature was a regex. The model wrote a sentence containing the answer, and the regex reached into that sentence and pulled the answer out. It had been written against the phrasing the model used at the time, and that phrasing had held steady for so long that everyone forgot the regex was fragile. Then a particular kind of user input nudged the model into phrasing the sentence a little differently. The answer was still in there. The regex, tuned to the old wording, matched nothing and returned empty.

The regex works until the phrasing moves

This is the trap of parsing natural language with a pattern. The model’s output is not a stable format. It is prose, and prose varies with the input. The regex was written against a snapshot of that prose, the examples the founder saw during development, and it encoded assumptions about word order, punctuation, and phrasing that the model was never promised to keep.

For months those assumptions held, which is the dangerous part. A regex that works for a long time earns a trust it did not deserve. Nobody flags it in review, because it has never broken. Its fragility is invisible right up until a user, doing nothing wrong, feeds in an input that shifts the model’s wording past what the pattern expects. Then it fails silently, because a regex that matches nothing does not throw. It just returns empty, and the feature quietly stops working for whoever triggered the new phrasing.

The user found the bug. Not a test, not an alert, a real person getting empty results. That is what “a bug waiting for a user” means. The failure was baked in from the day the regex was written. It just needed the right input to arrive, and the space of inputs is large enough that eventually one did.

Constrain the output instead

The way out is to stop extracting the answer from prose and start receiving it in a shape you defined:

  • Ask for structured output, not a sentence. Have the model return the answer as a named field in a structured response. There is no phrasing to match, because there is no prose to parse.
  • Use a validated schema. Define the fields and types you expect, and validate the response against them. A schema does not care how the model would have worded a sentence.
  • If you must parse text, parse a format you enforced. When structured output is not available, constrain the model to a strict, simple format and validate it. Do not reach into free-form prose and hope the wording holds.
  • Treat any regex over model prose as temporary. If one exists in your codebase, it is a latent failure. Plan to replace it before a user finds it for you.

The principle is that natural-language output is not a parse target. It varies by design, and a pattern written against yesterday’s phrasing is a bet that the phrasing never moves. That bet loses. Constraining the output removes the bet entirely, because you stop depending on how the model chooses to word things and start depending on a shape you control.

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, a regex sitting over model prose is one of the first things we flag. It is almost always a bug that has not gone off yet. We replace it with structured output and a validated schema, so the answer arrives in a field instead of a sentence, and the feature stops depending on wording that can shift under it. Finding this in a review is cheap. Finding it because a user got empty results and churned is not.

If a regex is reading your model’s prose, the bug is already written. A user just has not run it yet.