
Your Agent Returns Free-Form Text. Add `outputFormat` to Get Validated JSON Instead.
Chris Harper
2 min read
Sep 3, 2026 · 04:09 UTC
Add one field to your Claude Agents SDK query() call and the SDK validates the model's response against your Zod or Pydantic schema, re-prompting automatically on mismatch.
What you'll be able to do after this: define the exact shape of data you need — a BugReport, a FeaturePlan, an array of extracted TODOs with git blame — and get back a validated TypeScript or Python object instead of a string you'd have to parse yourself.
Why free-form text breaks pipelines: agents return unstructured text by default. That works for chat. It fails when the agent's output feeds a database, a UI component, or the next automated stage — because parsing "the model's summary" is fragile and breaks on edge cases.
The outputFormat option in TypeScript:
import { z } from "zod";
import { query } from "@anthropic-ai/claude-agent-sdk";
const BugReport = z.object({
file: z.string(),
line: z.number(),
severity: z.enum(["low", "medium", "high"]),
description: z.string()
});
// Draft-07 is required — Zod defaults to 2020-12, which the SDK rejects
const schema = z.toJSONSchema(BugReport, { target: "draft-7" });
for await (const message of query({
prompt: "Find the most critical bug in this codebase",
options: { outputFormat: { type: "json_schema", schema } }
})) {
if (message.type === "result" && message.subtype === "success" && message.structured_output) {
const bug = BugReport.safeParse(message.structured_output);
if (bug.success) console.log(`${bug.data.file}:${bug.data.line} — ${bug.data.severity}`);
}
}
Python uses output_format and Pydantic: output_format={"type": "json_schema", "schema": BugReport.model_json_schema()}. Pydantic targets draft-07 by default — no extra flag.
Three gotchas:
- Zod must target draft-07. Pass
{ target: "draft-7" }toz.toJSONSchema()or the SDK rejects the schema at startup with a named error. Before v2.1.205, an invalid schema was silently ignored. formatannotations are accepted but not enforced.{ "format": "email" }annotates, it doesn't validate.- Complex schemas fail more. Many required nested fields make the SDK's retry loop harder to close. Start flat — add nesting only when you need it.
On failure, the result subtype is error_max_structured_output_retries. Check message.errors to distinguish a schema mismatch from a model-fallback retraction mid-stream — the two have different root causes and different fixes.
Sources: Get structured output from agents — code.claude.com · Structured outputs — platform.claude.com · Structured Output from Claude Agent SDK Workflows — heyclau.de