Follow Discord
Sweep 22 Oct 2026 · 04:32Z Build v2.1.308 527 read Stable v2.1.308 Latest v2.1.308 Next v2.1.308 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One change · claude-code

Get structured output from agents changedagent-sdk/structured-outputs

Nearest release: v2.1.293, published 9 hours after upstream edited the page. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.

Upstream edited this page at 7 Oct 2026 07:30 UTC, give or take a minute or two: the time comes from Anthropic’s own sitemap rather than from a commit. This site recorded the change at 20 Oct 2026 18:37 UTC.

Upstream edited
Recorded here
Lines+399added
Lines−409removed
From line 12 where the diff opens
First seen 14 Aug 2026 this site's first read of the page
Recorded edits8to this page, all time

The whole hunk

from line 12, old and new numbered
/
lines

The two sides of this change are more than 400 edits apart, too far apart to line up, so this is the differ's own diff of it and the words inside a line are not marked.

from line 12
1212 
1313Consider a recipe app where an agent searches the web and brings back recipes. Without structured outputs, you get free-form text that you'd need to parse yourself. With structured outputs, you define the shape you want and get typed data you can use directly in your app.
1414 
15<AccordionGroup>
16 <Accordion title="Without structured outputs">
17 ```text theme={null}
18 Here's a classic chocolate chip cookie recipe!
19 
20 **Chocolate Chip Cookies**
21 Prep time: 15 minutes | Cook time: 10 minutes
22 
23 Ingredients:
24 - 2 1/4 cups all-purpose flour
25 - 1 cup butter, softened
26 ...
27 ```
28 
29 To use this in your app, you'd need to parse out the title, convert "15 minutes" to a number, separate ingredients from instructions, and handle inconsistent formatting across responses.
30 </Accordion>
31 
32 <Accordion title="With structured outputs">
33 ```jsonc theme={null}
34 {
35 "name": "Chocolate Chip Cookies",
36 "prep_time_minutes": 15,
37 "cook_time_minutes": 10,
38 "ingredients": [
39 { "item": "all-purpose flour", "amount": 2.25, "unit": "cups" },
40 { "item": "butter, softened", "amount": 1, "unit": "cup" }
41 // ...
42 ],
43 "steps": ["Preheat oven to 375°F", "Cream butter and sugar" /* ... */]
44 }
45 ```
46 
47 Typed data you can use directly in your UI.
48 </Accordion>
49</AccordionGroup>
15> **Without structured outputs**
16>
17> ```text theme={null}
18> Here's a classic chocolate chip cookie recipe!
19>
20> **Chocolate Chip Cookies**
21> Prep time: 15 minutes | Cook time: 10 minutes
22>
23> Ingredients:
24> - 2 1/4 cups all-purpose flour
25> - 1 cup butter, softened
26> ...
27> ```
28>
29> To use this in your app, you'd need to parse out the title, convert "15 minutes" to a number, separate ingredients from instructions, and handle inconsistent formatting across responses.
30 
31> **With structured outputs**
32>
33> ```jsonc theme={null}
34> {
35> "name": "Chocolate Chip Cookies",
36> "prep_time_minutes": 15,
37> "cook_time_minutes": 10,
38> "ingredients": [
39> { "item": "all-purpose flour", "amount": 2.25, "unit": "cups" },
40> { "item": "butter, softened", "amount": 1, "unit": "cup" }
41> // ...
42> ],
43> "steps": ["Preheat oven to 375°F", "Cream butter and sugar" /* ... */]
44> }
45> ```
46>
47> Typed data you can use directly in your UI.
5048 
5149## Quick start
5250 
from line 52
5452 
5553Before running the examples on this page, install the Claude Agent SDK by following the [quickstart](/docs/en/agent-sdk/quickstart#setup). The example below asks the agent to research Anthropic and return the company name, year founded, and headquarters as structured output.
5654 
57<CodeGroup>
58 ```typescript TypeScript theme={null}
59 import { query } from "@anthropic-ai/claude-agent-sdk";
60 
61 // Define the shape of data you want back
62 const schema = {
63 type: "object",
64 properties: {
65 company_name: { type: "string" },
66 founded_year: { type: "number" },
67 headquarters: { type: "string" }
55```typescript TypeScript theme={null}
56import { query } from "@anthropic-ai/claude-agent-sdk";
57 
58// Define the shape of data you want back
59const schema = {
60 type: "object",
61 properties: {
62 company_name: { type: "string" },
63 founded_year: { type: "number" },
64 headquarters: { type: "string" }
65 },
66 required: ["company_name"]
67};
68 
69try {
70 for await (const message of query({
71 prompt: "Research Anthropic and provide key company information",
72 options: {
73 outputFormat: {
74 type: "json_schema",
75 schema: schema
76 }
77 }
78 })) {
79 // The result message contains structured_output with validated data
80 if (message.type === "result" && message.subtype === "success" && message.structured_output) {
81 console.log(message.structured_output);
82 // { company_name: "Anthropic", founded_year: 2021, headquarters: "San Francisco, CA" }
83 }
84 }
85} catch (error) {
86 // A single-shot query() throws after yielding an error result, such as
87 // error_max_structured_output_retries; see the Error handling section.
88 console.error(`Session ended with an error: ${error}`);
89}
90```
91 
92```python Python theme={null}
93import asyncio
94from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
95 
96# Define the shape of data you want back
97schema = {
98 "type": "object",
99 "properties": {
100 "company_name": {"type": "string"},
101 "founded_year": {"type": "number"},
102 "headquarters": {"type": "string"},
68103 },
69 required: ["company_name"]
70 };
71 
72 try {
73 for await (const message of query({
74 prompt: "Research Anthropic and provide key company information",
75 options: {
76 outputFormat: {
77 type: "json_schema",
78 schema: schema
79 }
80 }
81 })) {
82 // The result message contains structured_output with validated data
83 if (message.type === "result" && message.subtype === "success" && message.structured_output) {
84 console.log(message.structured_output);
85 // { company_name: "Anthropic", founded_year: 2021, headquarters: "San Francisco, CA" }
86 }
87 }
88 } catch (error) {
89 // A single-shot query() throws after yielding an error result, such as
90 // error_max_structured_output_retries; see the Error handling section.
91 console.error(`Session ended with an error: ${error}`);
104 "required": ["company_name"],
105}
106 
107 
108async def main():
109 try:
110 async for message in query(
111 prompt="Research Anthropic and provide key company information",
112 options=ClaudeAgentOptions(
113 output_format={"type": "json_schema", "schema": schema}
114 ),
115 ):
116 # The result message contains structured_output with validated data
117 if isinstance(message, ResultMessage) and message.structured_output:
118 print(message.structured_output)
119 # {'company_name': 'Anthropic', 'founded_year': 2021, 'headquarters': 'San Francisco, CA'}
120 except Exception as error:
121 # A single-shot query() raises after yielding an error result, such as
122 # error_max_structured_output_retries; see the Error handling section.
123 print(f"Session ended with an error: {error}")
124 
125 
126asyncio.run(main())
127```
128 
129## Type-safe schemas with Zod and Pydantic
130 
131Instead of writing JSON Schema by hand, you can use [Zod](https://zod.dev/) (TypeScript) or [Pydantic](https://docs.pydantic.dev/latest/) (Python) to define your schema. These libraries generate the JSON Schema for you and let you parse the response into a fully-typed object you can use throughout your codebase with autocomplete and type checking.
132 
133The example below defines a schema for a feature implementation plan with a summary, list of steps (each with complexity level), and potential risks. The agent plans the feature and returns a typed `FeaturePlan` object. You can then access properties like `plan.summary` and iterate over `plan.steps` with full type safety.
134 
135The SDK validates schemas with JSON Schema draft-07, so schemas that declare a newer version are rejected. Zod targets draft 2020-12 by default, so pass `target: "draft-7"` when converting your schema.
136 
137```typescript TypeScript theme={null}
138import { z } from "zod";
139import { query } from "@anthropic-ai/claude-agent-sdk";
140 
141// Define schema with Zod
142const FeaturePlan = z.object({
143 feature_name: z.string(),
144 summary: z.string(),
145 steps: z.array(
146 z.object({
147 step_number: z.number(),
148 description: z.string(),
149 estimated_complexity: z.enum(["low", "medium", "high"])
150 })
151 ),
152 risks: z.array(z.string())
153});
154 
155type FeaturePlan = z.infer<typeof FeaturePlan>;
156 
157// Convert to JSON Schema using the draft-07 target the SDK expects
158const schema = z.toJSONSchema(FeaturePlan, { target: "draft-7" });
159 
160// Use in query
161try {
162 for await (const message of query({
163 prompt:
164 "Plan how to add dark mode support to a React app. Break it into implementation steps.",
165 options: {
166 outputFormat: {
167 type: "json_schema",
168 schema: schema
169 }
170 }
171 })) {
172 if (message.type === "result" && message.subtype === "success" && message.structured_output) {
173 // Validate and get fully typed result
174 const parsed = FeaturePlan.safeParse(message.structured_output);
175 if (parsed.success) {
176 const plan: FeaturePlan = parsed.data;
177 console.log(`Feature: ${plan.feature_name}`);
178 console.log(`Summary: ${plan.summary}`);
179 plan.steps.forEach((step) => {
180 console.log(`${step.step_number}. [${step.estimated_complexity}] ${step.description}`);
181 });
182 }
183 }
92184 }
93 ```
94 
95 ```python Python theme={null}
96 import asyncio
97 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
98 
99 # Define the shape of data you want back
100 schema = {
101 "type": "object",
102 "properties": {
103 "company_name": {"type": "string"},
104 "founded_year": {"type": "number"},
105 "headquarters": {"type": "string"},
106 },
107 "required": ["company_name"],
108 }
109 
110 
111 async def main():
112 try:
113 async for message in query(
114 prompt="Research Anthropic and provide key company information",
115 options=ClaudeAgentOptions(
116 output_format={"type": "json_schema", "schema": schema}
117 ),
118 ):
119 # The result message contains structured_output with validated data
120 if isinstance(message, ResultMessage) and message.structured_output:
121 print(message.structured_output)
122 # {'company_name': 'Anthropic', 'founded_year': 2021, 'headquarters': 'San Francisco, CA'}
123 except Exception as error:
124 # A single-shot query() raises after yielding an error result, such as
125 # error_max_structured_output_retries; see the Error handling section.
126 print(f"Session ended with an error: {error}")
127 
128 
129 asyncio.run(main())
130 ```
131</CodeGroup>
132 
133## Type-safe schemas with Zod and Pydantic
134 
135Instead of writing JSON Schema by hand, you can use [Zod](https://zod.dev/) (TypeScript) or [Pydantic](https://docs.pydantic.dev/latest/) (Python) to define your schema. These libraries generate the JSON Schema for you and let you parse the response into a fully-typed object you can use throughout your codebase with autocomplete and type checking.
136 
137The example below defines a schema for a feature implementation plan with a summary, list of steps (each with complexity level), and potential risks. The agent plans the feature and returns a typed `FeaturePlan` object. You can then access properties like `plan.summary` and iterate over `plan.steps` with full type safety.
138 
139The SDK validates schemas with JSON Schema draft-07, so schemas that declare a newer version are rejected. Zod targets draft 2020-12 by default, so pass `target: "draft-7"` when converting your schema.
140 
141<CodeGroup>
142 ```typescript TypeScript theme={null}
143 import { z } from "zod";
144 import { query } from "@anthropic-ai/claude-agent-sdk";
145 
146 // Define schema with Zod
147 const FeaturePlan = z.object({
148 feature_name: z.string(),
149 summary: z.string(),
150 steps: z.array(
151 z.object({
152 step_number: z.number(),
153 description: z.string(),
154 estimated_complexity: z.enum(["low", "medium", "high"])
155 })
156 ),
157 risks: z.array(z.string())
158 });
159 
160 type FeaturePlan = z.infer<typeof FeaturePlan>;
161 
162 // Convert to JSON Schema using the draft-07 target the SDK expects
163 const schema = z.toJSONSchema(FeaturePlan, { target: "draft-7" });
164 
165 // Use in query
166 try {
167 for await (const message of query({
168 prompt:
169 "Plan how to add dark mode support to a React app. Break it into implementation steps.",
170 options: {
171 outputFormat: {
172 type: "json_schema",
173 schema: schema
174 }
175 }
176 })) {
177 if (message.type === "result" && message.subtype === "success" && message.structured_output) {
178 // Validate and get fully typed result
179 const parsed = FeaturePlan.safeParse(message.structured_output);
180 if (parsed.success) {
181 const plan: FeaturePlan = parsed.data;
182 console.log(`Feature: ${plan.feature_name}`);
183 console.log(`Summary: ${plan.summary}`);
184 plan.steps.forEach((step) => {
185 console.log(`${step.step_number}. [${step.estimated_complexity}] ${step.description}`);
186 });
187 }
188 }
189 }
190 } catch (error) {
191 // A single-shot query() throws after yielding an error result, such as
192 // error_max_structured_output_retries; see the Error handling section.
193 console.error(`Session ended with an error: ${error}`);
194 }
195 ```
196 
197 ```python Python theme={null}
198 import asyncio
199 from pydantic import BaseModel
200 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
201 
202 
203 class Step(BaseModel):
204 step_number: int
205 description: str
206 estimated_complexity: str # 'low', 'medium', 'high'
207 
208 
209 class FeaturePlan(BaseModel):
210 feature_name: str
211 summary: str
212 steps: list[Step]
213 risks: list[str]
214 
215 
216 async def main():
217 try:
218 async for message in query(
219 prompt="Plan how to add dark mode support to a React app. Break it into implementation steps.",
220 options=ClaudeAgentOptions(
221 output_format={
222 "type": "json_schema",
223 "schema": FeaturePlan.model_json_schema(),
224 }
225 ),
226 ):
227 if isinstance(message, ResultMessage) and message.structured_output:
228 # Validate and get fully typed result
229 plan = FeaturePlan.model_validate(message.structured_output)
230 print(f"Feature: {plan.feature_name}")
231 print(f"Summary: {plan.summary}")
232 for step in plan.steps:
233 print(
234 f"{step.step_number}. [{step.estimated_complexity}] {step.description}"
235 )
236 except Exception as error:
237 # A single-shot query() raises after yielding an error result, such as
238 # error_max_structured_output_retries; see the Error handling section.
239 print(f"Session ended with an error: {error}")
240 
241 
242 asyncio.run(main())
243 ```
244</CodeGroup>
185} catch (error) {
186 // A single-shot query() throws after yielding an error result, such as
187 // error_max_structured_output_retries; see the Error handling section.
188 console.error(`Session ended with an error: ${error}`);
189}
190```
191 
192```python Python theme={null}
193import asyncio
194from pydantic import BaseModel
195from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
196 
197 
198class Step(BaseModel):
199 step_number: int
200 description: str
201 estimated_complexity: str # 'low', 'medium', 'high'
202 
203 
204class FeaturePlan(BaseModel):
205 feature_name: str
206 summary: str
207 steps: list[Step]
208 risks: list[str]
209 
210 
211async def main():
212 try:
213 async for message in query(
214 prompt="Plan how to add dark mode support to a React app. Break it into implementation steps.",
215 options=ClaudeAgentOptions(
216 output_format={
217 "type": "json_schema",
218 "schema": FeaturePlan.model_json_schema(),
219 }
220 ),
221 ):
222 if isinstance(message, ResultMessage) and message.structured_output:
223 # Validate and get fully typed result
224 plan = FeaturePlan.model_validate(message.structured_output)
225 print(f"Feature: {plan.feature_name}")
226 print(f"Summary: {plan.summary}")
227 for step in plan.steps:
228 print(
229 f"{step.step_number}. [{step.estimated_complexity}] {step.description}"
230 )
231 except Exception as error:
232 # A single-shot query() raises after yielding an error result, such as
233 # error_max_structured_output_retries; see the Error handling section.
234 print(f"Session ended with an error: {error}")
235 
236 
237asyncio.run(main())
238```
245239 
246240## Output format configuration
247241 
from line 256
262256 
263257The schema includes optional fields (`author` and `date`) since git blame information might not be available for all files. The agent fills in what it can find and omits the rest.
264258 
265<CodeGroup>
266 ```typescript TypeScript theme={null}
267 import { query } from "@anthropic-ai/claude-agent-sdk";
268 
269 // Define structure for TODO extraction
270 const todoSchema = {
271 type: "object",
272 properties: {
273 todos: {
274 type: "array",
275 items: {
276 type: "object",
277 properties: {
278 text: { type: "string" },
279 file: { type: "string" },
280 line: { type: "number" },
281 author: { type: "string" },
282 date: { type: "string" }
283 },
284 required: ["text", "file", "line"]
259```typescript TypeScript theme={null}
260import { query } from "@anthropic-ai/claude-agent-sdk";
261 
262// Define structure for TODO extraction
263const todoSchema = {
264 type: "object",
265 properties: {
266 todos: {
267 type: "array",
268 items: {
269 type: "object",
270 properties: {
271 text: { type: "string" },
272 file: { type: "string" },
273 line: { type: "number" },
274 author: { type: "string" },
275 date: { type: "string" }
276 },
277 required: ["text", "file", "line"]
278 }
279 },
280 total_count: { type: "number" }
281 },
282 required: ["todos", "total_count"]
283};
284 
285// Agent uses Grep to find TODOs, Bash to get git blame info
286try {
287 for await (const message of query({
288 prompt: "Find all TODO comments in this codebase and identify who added them",
289 options: {
290 outputFormat: {
291 type: "json_schema",
292 schema: todoSchema
293 }
294 }
295 })) {
296 if (message.type === "result" && message.subtype === "success" && message.structured_output) {
297 const data = message.structured_output as { total_count: number; todos: Array<{ file: string; line: number; text: string; author?: string; date?: string }> };
298 console.log(`Found ${data.total_count} TODOs`);
299 data.todos.forEach((todo) => {
300 console.log(`${todo.file}:${todo.line} - ${todo.text}`);
301 if (todo.author) {
302 console.log(` Added by ${todo.author} on ${todo.date}`);
285303 }
286 },
287 total_count: { type: "number" }
304 });
305 }
306 }
307} catch (error) {
308 // A single-shot query() throws after yielding an error result, such as
309 // error_max_structured_output_retries; see the Error handling section.
310 console.error(`Session ended with an error: ${error}`);
311}
312```
313 
314```python Python theme={null}
315import asyncio
316from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
317 
318# Define structure for TODO extraction
319todo_schema = {
320 "type": "object",
321 "properties": {
322 "todos": {
323 "type": "array",
324 "items": {
325 "type": "object",
326 "properties": {
327 "text": {"type": "string"},
328 "file": {"type": "string"},
329 "line": {"type": "number"},
330 "author": {"type": "string"},
331 "date": {"type": "string"},
332 },
333 "required": ["text", "file", "line"],
334 },
335 },
336 "total_count": {"type": "number"},
288337 },
289 required: ["todos", "total_count"]
290 };
291 
292 // Agent uses Grep to find TODOs, Bash to get git blame info
293 try {
294 for await (const message of query({
295 prompt: "Find all TODO comments in this codebase and identify who added them",
296 options: {
297 outputFormat: {
298 type: "json_schema",
299 schema: todoSchema
300 }
301 }
302 })) {
303 if (message.type === "result" && message.subtype === "success" && message.structured_output) {
304 const data = message.structured_output as { total_count: number; todos: Array<{ file: string; line: number; text: string; author?: string; date?: string }> };
305 console.log(`Found ${data.total_count} TODOs`);
306 data.todos.forEach((todo) => {
307 console.log(`${todo.file}:${todo.line} - ${todo.text}`);
308 if (todo.author) {
309 console.log(` Added by ${todo.author} on ${todo.date}`);
310 }
311 });
312 }
313 }
314 } catch (error) {
315 // A single-shot query() throws after yielding an error result, such as
316 // error_max_structured_output_retries; see the Error handling section.
317 console.error(`Session ended with an error: ${error}`);
318 }
319 ```
320 
321 ```python Python theme={null}
322 import asyncio
323 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
324 
325 # Define structure for TODO extraction
326 todo_schema = {
327 "type": "object",
328 "properties": {
329 "todos": {
330 "type": "array",
331 "items": {
332 "type": "object",
333 "properties": {
334 "text": {"type": "string"},
335 "file": {"type": "string"},
336 "line": {"type": "number"},
337 "author": {"type": "string"},
338 "date": {"type": "string"},
339 },
340 "required": ["text", "file", "line"],
341 },
342 },
343 "total_count": {"type": "number"},
344 },
345 "required": ["todos", "total_count"],
346 }
347 
348 
349 async def main():
350 # Agent uses Grep to find TODOs, Bash to get git blame info
351 try:
352 async for message in query(
353 prompt="Find all TODO comments in this codebase and identify who added them",
354 options=ClaudeAgentOptions(
355 output_format={"type": "json_schema", "schema": todo_schema}
356 ),
357 ):
358 if isinstance(message, ResultMessage) and message.structured_output:
359 data = message.structured_output
360 print(f"Found {data['total_count']} TODOs")
361 for todo in data["todos"]:
362 print(f"{todo['file']}:{todo['line']} - {todo['text']}")
363 if "author" in todo:
364 print(f" Added by {todo['author']} on {todo['date']}")
365 except Exception as error:
366 # A single-shot query() raises after yielding an error result, such as
367 # error_max_structured_output_retries; see the Error handling section.
368 print(f"Session ended with an error: {error}")
369 
370 
371 asyncio.run(main())
372 ```
373</CodeGroup>
338 "required": ["todos", "total_count"],
339}
340 
341 
342async def main():
343 # Agent uses Grep to find TODOs, Bash to get git blame info
344 try:
345 async for message in query(
346 prompt="Find all TODO comments in this codebase and identify who added them",
347 options=ClaudeAgentOptions(
348 output_format={"type": "json_schema", "schema": todo_schema}
349 ),
350 ):
351 if isinstance(message, ResultMessage) and message.structured_output:
352 data = message.structured_output
353 print(f"Found {data['total_count']} TODOs")
354 for todo in data["todos"]:
355 print(f"{todo['file']}:{todo['line']} - {todo['text']}")
356 if "author" in todo:
357 print(f" Added by {todo['author']} on {todo['date']}")
358 except Exception as error:
359 # A single-shot query() raises after yielding an error result, such as
360 # error_max_structured_output_retries; see the Error handling section.
361 print(f"Session ended with an error: {error}")
362 
363 
364asyncio.run(main())
365```
374366 
375367## Error handling
376368 
from line 377
385377 
386378A result can also end with subtype `success` but no `structured_output` value, for example when the run completes without the agent producing a structured output. Treat that case as a failure as well. The troubleshooting entry [structured\_output is None but the result says success](/docs/en/agent-sdk/troubleshooting#structured_output-is-none-but-the-result-says-success) covers this case. The example below treats a result as successful only when the `subtype` is `success` and `structured_output` is present, and handles every other result as a failure:
387379 
388<CodeGroup>
389 ```typescript TypeScript theme={null}
390 import { query } from "@anthropic-ai/claude-agent-sdk";
391 
392 const contactSchema = {
393 type: "object",
394 properties: {
395 name: { type: "string" },
396 email: { type: "string" }
380```typescript TypeScript theme={null}
381import { query } from "@anthropic-ai/claude-agent-sdk";
382 
383const contactSchema = {
384 type: "object",
385 properties: {
386 name: { type: "string" },
387 email: { type: "string" }
388 },
389 required: ["name"]
390};
391 
392try {
393 for await (const msg of query({
394 prompt: "Extract contact info from the document",
395 options: {
396 outputFormat: {
397 type: "json_schema",
398 schema: contactSchema
399 }
400 }
401 })) {
402 if (msg.type === "result") {
403 if (msg.subtype === "success" && msg.structured_output) {
404 // Use the validated output
405 console.log(msg.structured_output);
406 } else if (msg.subtype === "error_max_structured_output_retries") {
407 console.error("Could not produce valid output");
408 } else {
409 console.error("Run ended without a structured output");
410 }
411 }
412 }
413} catch (error) {
414 // A single-shot query() throws after yielding an error result. If the
415 // failure was an error result, the error subtype branches above have
416 // already run; connection or process failures yield no result message.
417 console.log(`Session ended with an error: ${error}`);
418}
419```
420 
421```python Python theme={null}
422import asyncio
423from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
424 
425contact_schema = {
426 "type": "object",
427 "properties": {
428 "name": {"type": "string"},
429 "email": {"type": "string"},
397430 },
398 required: ["name"]
399 };
400 
401 try {
402 for await (const msg of query({
403 prompt: "Extract contact info from the document",
404 options: {
405 outputFormat: {
406 type: "json_schema",
407 schema: contactSchema
408 }
409 }
410 })) {
411 if (msg.type === "result") {
412 if (msg.subtype === "success" && msg.structured_output) {
413 // Use the validated output
414 console.log(msg.structured_output);
415 } else if (msg.subtype === "error_max_structured_output_retries") {
416 console.error("Could not produce valid output");
417 } else {
418 console.error("Run ended without a structured output");
419 }
420 }
421 }
422 } catch (error) {
423 // A single-shot query() throws after yielding an error result. If the
424 // failure was an error result, the error subtype branches above have
425 // already run; connection or process failures yield no result message.
426 console.log(`Session ended with an error: ${error}`);
427 }
428 ```
429 
430 ```python Python theme={null}
431 import asyncio
432 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
433 
434 contact_schema = {
435 "type": "object",
436 "properties": {
437 "name": {"type": "string"},
438 "email": {"type": "string"},
439 },
440 "required": ["name"],
441 }
442 
443 
444 async def main():
445 try:
446 async for message in query(
447 prompt="Extract contact info from the document",
448 options=ClaudeAgentOptions(
449 output_format={"type": "json_schema", "schema": contact_schema}
450 ),
451 ):
452 if isinstance(message, ResultMessage):
453 if message.subtype == "success" and message.structured_output:
454 # Use the validated output
455 print(message.structured_output)
456 elif message.subtype == "error_max_structured_output_retries":
457 print("Could not produce valid output")
458 else:
459 print("Run ended without a structured output")
460 except Exception as error:
461 # A single-shot query() raises after yielding an error result. If the
462 # failure was an error result, the error subtype branches above have
463 # already run; connection or process failures yield no result message.
464 print(f"Session ended with an error: {error}")
465 
466 
467 asyncio.run(main())
468 ```
469</CodeGroup>
431 "required": ["name"],
432}
433 
434 
435async def main():
436 try:
437 async for message in query(
438 prompt="Extract contact info from the document",
439 options=ClaudeAgentOptions(
440 output_format={"type": "json_schema", "schema": contact_schema}
441 ),
442 ):
443 if isinstance(message, ResultMessage):
444 if message.subtype == "success" and message.structured_output:
445 # Use the validated output
446 print(message.structured_output)
447 elif message.subtype == "error_max_structured_output_retries":
448 print("Could not produce valid output")
449 else:
450 print("Run ended without a structured output")
451 except Exception as error:
452 # A single-shot query() raises after yielding an error result. If the
453 # failure was an error result, the error subtype branches above have
454 # already run; connection or process failures yield no result message.
455 print(f"Session ended with an error: {error}")
456 
457 
458asyncio.run(main())
459```
470460 
471461**Tips for avoiding errors:**
472462 
473463 
Feedback