Never trust an LLM's output directly. Here's the validation layer I put on every agent. A developer built a validation layer for LLM agent outputs that catches structural and semantic errors before code acts on the data. The three-stage pipeline—parse, validate, classify—uses Zod schemas to enforce type and semantic constraints, returning a discriminated union that forces callers to handle failure paths. The approach addresses common failure modes where models emit valid JSON that is structurally or semantically incorrect. Here's a failure mode I've seen in nearly every AI agent codebase I've reviewed: the agent receives a model response, trusts the JSON it contains, and calls .result.items 0 .id — which throws Cannot read properties of undefined at 2 AM because the model returned {"result": null} on an edge case. The model didn't hallucinate the content. It hallucinated the structure . This is surprisingly common, and the fix isn't "use a better prompt." The fix is a validation layer that runs between the raw model output and the code that acts on it. Claude and GPT-4 both support structured output modes that constrain the model to emit valid JSON matching a given schema. This is genuinely useful and you should use it. But it doesn't fully solve the problem, for two reasons: 1. JSON-valid is not semantically valid. The model can emit perfectly valid JSON that conforms to your schema and still be wrong. A string field that should be a UUID might contain a made-up identifier that fails a database lookup. An integer field labeled confidence score might be 847 when your code expects a 0-1 float. The schema enforces types, not semantics. 2. Not all LLM calls use structured output. If you're doing multi-step reasoning, chain-of-thought steps, tool call parsing, or processing outputs from models that don't support native JSON mode, you're parsing free-text responses. You need to handle that robustly. Every agent call I build now goes through three stages: raw model output ↓ PARSE – extract the structure from the text ↓ VALIDATE – assert the structure matches expectations ↓ CLASSIFY – categorize the outcome so the caller can handle it Here's the TypeScript implementation I actually use: js import { z } from "zod"; // 1. Define the schema for what you expect const AnalysisResultSchema = z.object { sentiment: z.enum "positive", "negative", "neutral" , confidence: z.number .min 0 .max 1 , key points: z.array z.string .min 1 .max 10 , action required: z.boolean , follow up: z.string .optional , } ; type AnalysisResult = z.infer