from line 1846
18461846
18471847## Error Types
18481848
1849The types below define what your code catches. For entries keyed to the error messages these types raise, with the cause and fix for each, see [Troubleshooting](/docs/en/agent-sdk/troubleshooting).
1850
18491851### `ClaudeSDKError`
18501852
18511853Base exception class for all SDK errors.
from line 1971
19691971* `tool_use_id`: Optional tool use identifier (for tool-related hooks)
19701972* `context`: Hook context with additional information
19711973
1972Returns a [`HookJSONOutput`](#hookjsonoutput) that may contain:
1974Returns a [`HookJSONOutput`](#hookjsonoutput).
19731975
1974* `decision`: `"block"` to block the action
1975* `systemMessage`: warning message shown to the user
1976* `hookSpecificOutput`: Hook-specific output data
1977
19781976### `HookContext`
19791977
19801978Context information passed to hook callbacks.
from line 2860
28622860**Tool name:** `TodoWrite`
28632861
28642862<Note>
2865 On Python Agent SDK 0.2.139 and later, the following tools aren't available on Opus 4.8, Sonnet 5, Fable 5, Mythos 5, or later versions of those families unless you opt in:
2863 On Python Agent SDK 0.2.139 and later, the following restriction applies.
28662864
2865 The following tools aren't available on Opus 4.8, Sonnet 5, Fable 5, Mythos 5, or later versions of those families unless you opt in:
2866
28672867 * `TodoWrite`
28682868 * `TaskCreate`
28692869 * `TaskGet`
from line 3129
31293129}
31303130```
31313131
3132## Advanced Features with ClaudeSDKClient
3132## Build a continuous conversation interface
31333133
3134### Building a Continuous Conversation Interface
3134The following example keeps one `ClaudeSDKClient` connected across turns, so Claude remembers earlier messages. Type `new` to disconnect and reconnect for a fresh session, or `exit` to end the conversation.
31353135
31363136```python theme={null}
31373137from claude_agent_sdk import (
from line 3210
32103210asyncio.run(main())
32113211```
32123212
3213### Using Hooks for Behavior Modification
3213## Error handling
32143214
3215```python theme={null}
3216from claude_agent_sdk import (
3217 ClaudeSDKClient,
3218 ClaudeAgentOptions,
3219 HookMatcher,
3220 HookContext,
3221)
3222import asyncio
3223from typing import Any
3215The following example wraps a `query()` call in handlers for four of the [error types](#error-types) the SDK raises.
32243216
3225
3226async def pre_tool_logger(
3227 input_data: dict[str, Any], tool_use_id: str | None, context: HookContext
3228) -> dict[str, Any]:
3229 """Log all tool usage before execution."""
3230 tool_name = input_data.get("tool_name", "unknown")
3231 print(f"[PRE-TOOL] About to use: {tool_name}")
3232
3233 # You can modify or block the tool execution here
3234 if tool_name == "Bash" and "rm -rf" in str(input_data.get("tool_input", {})):
3235 return {
3236 "hookSpecificOutput": {
3237 "hookEventName": "PreToolUse",
3238 "permissionDecision": "deny",
3239 "permissionDecisionReason": "Dangerous command blocked",
3240 }
3241 }
3242 return {}
3243
3244
3245async def post_tool_logger(
3246 input_data: dict[str, Any], tool_use_id: str | None, context: HookContext
3247) -> dict[str, Any]:
3248 """Log results after tool execution."""
3249 tool_name = input_data.get("tool_name", "unknown")
3250 print(f"[POST-TOOL] Completed: {tool_name}")
3251 return {}
3252
3253
3254async def user_prompt_modifier(
3255 input_data: dict[str, Any], tool_use_id: str | None, context: HookContext
3256) -> dict[str, Any]:
3257 """Add context to user prompts."""
3258 original_prompt = input_data.get("prompt", "")
3259
3260 # Add a timestamp as additional context for Claude to see
3261 from datetime import datetime
3262
3263 timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
3264
3265 return {
3266 "hookSpecificOutput": {
3267 "hookEventName": "UserPromptSubmit",
3268 "additionalContext": f"[Submitted at {timestamp}] Original prompt: {original_prompt}",
3269 }
3270 }
3271
3272
3273async def main():
3274 options = ClaudeAgentOptions(
3275 hooks={
3276 "PreToolUse": [
3277 HookMatcher(hooks=[pre_tool_logger]),
3278 HookMatcher(matcher="Bash", hooks=[pre_tool_logger]),
3279 ],
3280 "PostToolUse": [HookMatcher(hooks=[post_tool_logger])],
3281 "UserPromptSubmit": [HookMatcher(hooks=[user_prompt_modifier])],
3282 },
3283 allowed_tools=["Read", "Write", "Bash"],
3284 )
3285
3286 async with ClaudeSDKClient(options=options) as client:
3287 await client.query("List files in current directory")
3288
3289 async for message in client.receive_response():
3290 # Hooks will automatically log tool usage
3291 pass
3292
3293
3294asyncio.run(main())
3295```
3296
3297### Real-time Progress Monitoring
3298
3299```python theme={null}
3300from claude_agent_sdk import (
3301 ClaudeSDKClient,
3302 ClaudeAgentOptions,
3303 AssistantMessage,
3304 ToolUseBlock,
3305 ToolResultBlock,
3306 TextBlock,
3307)
3308import asyncio
3309
3310
3311async def monitor_progress():
3312 options = ClaudeAgentOptions(
3313 allowed_tools=["Write", "Bash"], permission_mode="acceptEdits"
3314 )
3315
3316 async with ClaudeSDKClient(options=options) as client:
3317 await client.query("Create 5 Python files with different sorting algorithms")
3318
3319 # Monitor progress in real-time
3320 async for message in client.receive_response():
3321 if isinstance(message, AssistantMessage):
3322 for block in message.content:
3323 if isinstance(block, ToolUseBlock):
3324 if block.name == "Write":
3325 file_path = block.input.get("file_path", "")
3326 print(f"Creating: {file_path}")
3327 elif isinstance(block, ToolResultBlock):
3328 print("Completed tool execution")
3329 elif isinstance(block, TextBlock):
3330 print(f"Claude says: {block.text[:100]}...")
3331
3332 print("Task completed!")
3333
3334
3335asyncio.run(monitor_progress())
3336```
3337
3338## Example Usage
3339
3340### Basic file operations (using query)
3341
3342```python theme={null}
3343from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock
3344import asyncio
3345
3346
3347async def create_project():
3348 options = ClaudeAgentOptions(
3349 allowed_tools=["Read", "Write", "Bash"],
3350 permission_mode="acceptEdits",
3351 )
3352
3353 async for message in query(
3354 prompt="Create a Python project structure with setup.py", options=options
3355 ):
3356 if isinstance(message, AssistantMessage):
3357 for block in message.content:
3358 if isinstance(block, ToolUseBlock):
3359 print(f"Using tool: {block.name}")
3360
3361
3362asyncio.run(create_project())
3363```
3364
3365### Error handling
3366
33673217This example catches [`ResultError`](#resulterror), which requires Python Agent SDK 0.2.140 or later.
33683218
33693219```python theme={null}
from line 3253
34033253asyncio.run(main())
34043254```
34053255
3406### Using custom tools with ClaudeSDKClient
3407
3408```python theme={null}
3409from claude_agent_sdk import (
3410 ClaudeSDKClient,
3411 ClaudeAgentOptions,
3412 tool,
3413 create_sdk_mcp_server,
3414 AssistantMessage,
3415 TextBlock,
3416)
3417import asyncio
3418from typing import Any
3419
3420
3421# Define custom tools with @tool decorator
3422@tool("calculate", "Perform mathematical calculations", {"expression": str})
3423async def calculate(args: dict[str, Any]) -> dict[str, Any]:
3424 try:
3425 result = eval(args["expression"], {"__builtins__": {}})
3426 return {"content": [{"type": "text", "text": f"Result: {result}"}]}
3427 except Exception as e:
3428 return {
3429 "content": [{"type": "text", "text": f"Error: {str(e)}"}],
3430 "is_error": True,
3431 }
3432
3433
3434@tool("get_time", "Get current time", {})
3435async def get_time(args: dict[str, Any]) -> dict[str, Any]:
3436 from datetime import datetime
3437
3438 current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
3439 return {"content": [{"type": "text", "text": f"Current time: {current_time}"}]}
3440
3441
3442async def main():
3443 # Create SDK MCP server with custom tools
3444 my_server = create_sdk_mcp_server(
3445 name="utilities", version="1.0.0", tools=[calculate, get_time]
3446 )
3447
3448 # Configure options with the server
3449 options = ClaudeAgentOptions(
3450 mcp_servers={"utils": my_server},
3451 allowed_tools=["mcp__utils__calculate", "mcp__utils__get_time"],
3452 )
3453
3454 # Use ClaudeSDKClient for interactive tool usage
3455 async with ClaudeSDKClient(options=options) as client:
3456 await client.query("What's 123 * 456?")
3457
3458 # Process calculation response
3459 async for message in client.receive_response():
3460 if isinstance(message, AssistantMessage):
3461 for block in message.content:
3462 if isinstance(block, TextBlock):
3463 print(f"Calculation: {block.text}")
3464
3465 # Follow up with time query
3466 await client.query("What time is it now?")
3467
3468 async for message in client.receive_response():
3469 if isinstance(message, AssistantMessage):
3470 for block in message.content:
3471 if isinstance(block, TextBlock):
3472 print(f"Time: {block.text}")
3473
3474
3475asyncio.run(main())
3476```
3477
34783256## Sandbox Configuration
34793257
34803258### `SandboxSettings`
from line 3450
36723450
36733451* [SDK overview](/docs/en/agent-sdk/overview) - General SDK concepts
36743452* [TypeScript SDK reference](/docs/en/agent-sdk/typescript) - TypeScript SDK documentation
3453* [Custom tools](/docs/en/agent-sdk/custom-tools) - Define in-process MCP tools for Claude to call
36753454* [CLI reference](/docs/en/cli-reference) - Command-line interface
36763455* [Common workflows](/docs/en/common-workflows) - Step-by-step guides
36773456