CloudCodeTree LogoCloudCodeTree
AI NewsTutorialsAbout
CloudCodeTree Logo
CloudCodeTree
  • AI News
  • Tutorials
  • About
← Back to AI News
Your Agent Returns Free-Form Text. Add `outputFormat` to Get Validated JSON Instead.

Your Agent Returns Free-Form Text. Add `outputFormat` to Get Validated JSON Instead.

Chris Harper

2 min read

Sep 3, 2026 · 04:09 UTC

AI
Tutorial
Agents
Best Practices

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:

  1. Zod must target draft-07. Pass { target: "draft-7" } to z.toJSONSchema() or the SDK rejects the schema at startup with a named error. Before v2.1.205, an invalid schema was silently ignored.
  2. format annotations are accepted but not enforced. { "format": "email" } annotates, it doesn't validate.
  3. 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