Structured output fails for a small number of recurring reasons, and knowing which one you are looking at usually tells you whether to change the schema or the prompt.
The most common is a type mismatch that looks correct to a human. A model asked for a number frequently returns the string "0.87". The JSON is well formed and the value is right, but a strict validator rejects it. Either coerce on your side deliberately, or make the description explicit that the value must be an unquoted number.
The second is invented properties. Models add fields that seem helpful — a confidence score, an explanation, a reasoning trace — that your schema never declared. Without additionalProperties set to false these pass validation silently and flow into your code unnoticed. With it set, they become a visible error, which is almost always what you want.
The third is a missing required field, usually because the model had nothing sensible to put there. This is a prompt problem rather than a schema problem: if a field is genuinely optional for some inputs, marking it required guarantees intermittent failures.
The fourth is wrapping. The payload is correct but arrives inside a markdown code fence with a sentence of preamble. This is a parsing problem, not a validation one, and it is solved by extracting the JSON rather than by changing the schema.
The fifth is enum drift, where a model returns a plausible value outside your allowed set — "very positive" when you defined positive, neutral and negative. Tightening the description and listing the allowed values in the prompt as well as the schema usually resolves it.
A practical rule: validate every response in development, log which of these five categories each failure falls into, and you will know within a day whether your problem is the schema, the prompt or the model.