docs
0
# Error Handling
1
2
Exception types for handling SDK errors including connection issues, process failures, and parsing errors. All SDK exceptions inherit from the base `ClaudeSDKError` class.
3
4
## Capabilities
5
6
### Base Exception
7
8
Base exception for all Claude SDK errors.
9
10
```python { .api }
11
class ClaudeSDKError(Exception):
12
"""Base exception for all Claude SDK errors."""
13
```
14
15
All SDK-specific exceptions inherit from `ClaudeSDKError`, allowing you to catch all SDK errors with a single except clause.
16
17
**Usage Example:**
18
19
```python
20
from claude_agent_sdk import query, ClaudeSDKError
21
22
try:
23
async for msg in query(prompt="Hello"):
24
print(msg)
25
except ClaudeSDKError as e:
26
print(f"SDK error: {e}")
27
```
28
29
### Connection Errors
30
31
#### CLIConnectionError
32
33
Raised when unable to connect to Claude Code.
34
35
```python { .api }
36
class CLIConnectionError(ClaudeSDKError):
37
"""Raised when unable to connect to Claude Code."""
38
```
39
40
**Common Causes:**
41
42
- CLI process failed to start
43
- CLI crashed during initialization
44
- Invalid options or configuration
45
- Communication protocol issues
46
47
**Usage Example:**
48
49
```python
50
from claude_agent_sdk import ClaudeSDKClient, CLIConnectionError
51
52
try:
53
async with ClaudeSDKClient() as client:
54
await client.query("Hello")
55
async for msg in client.receive_response():
56
print(msg)
57
except CLIConnectionError as e:
58
print(f"Failed to connect to Claude Code: {e}")
59
```
60
61
#### CLINotFoundError
62
63
Raised when Claude Code CLI is not found or not installed.
64
65
```python { .api }
66
class CLINotFoundError(CLIConnectionError):
67
"""Raised when Claude Code is not found or not installed."""
68
69
def __init__(
70
self,
71
message: str = "Claude Code not found",
72
cli_path: str | None = None
73
):
74
"""
75
Initialize CLINotFoundError.
76
77
Args:
78
message: Error message
79
cli_path: Path where CLI was expected
80
"""
81
```
82
83
**Note:** The `cli_path` parameter is incorporated into the error message but not stored as a separate attribute.
84
85
**Common Causes:**
86
87
- Bundled CLI not found (installation issue)
88
- Custom `cli_path` points to non-existent file
89
- CLI executable not in PATH
90
- Insufficient permissions to execute CLI
91
92
**Usage Example:**
93
94
```python
95
from claude_agent_sdk import query, ClaudeAgentOptions, CLINotFoundError
96
97
try:
98
options = ClaudeAgentOptions(cli_path="/custom/path/claude")
99
async for msg in query(prompt="Hello", options=options):
100
print(msg)
101
except CLINotFoundError as e:
102
print(f"Claude Code CLI not found: {e}")
103
# The cli_path is included in the error message
104
```
105
106
**Resolution:**
107
108
```python
109
from claude_agent_sdk import ClaudeAgentOptions, CLINotFoundError
110
import shutil
111
112
try:
113
# Try custom path first
114
options = ClaudeAgentOptions(cli_path="/usr/local/bin/claude")
115
async for msg in query(prompt="Hello", options=options):
116
print(msg)
117
except CLINotFoundError:
118
# Fall back to default bundled CLI
119
print("Custom CLI not found, using bundled version")
120
async for msg in query(prompt="Hello"):
121
print(msg)
122
```
123
124
### Process Errors
125
126
#### ProcessError
127
128
Raised when the CLI process fails.
129
130
```python { .api }
131
class ProcessError(ClaudeSDKError):
132
"""Raised when the CLI process fails."""
133
134
def __init__(
135
self,
136
message: str,
137
exit_code: int | None = None,
138
stderr: str | None = None
139
):
140
"""
141
Initialize ProcessError.
142
143
Args:
144
message: Error message
145
exit_code: Process exit code
146
stderr: Process stderr output
147
"""
148
149
exit_code: int | None
150
stderr: str | None
151
```
152
153
**Attributes:**
154
155
- `exit_code` (int | None): Exit code of the failed process.
156
- `stderr` (str | None): Standard error output from the process.
157
158
**Common Causes:**
159
160
- CLI crashed during execution
161
- Invalid arguments or options
162
- Resource exhaustion (memory, disk)
163
- Permission denied for operations
164
- API errors from Anthropic
165
166
**Usage Example:**
167
168
```python
169
from claude_agent_sdk import query, ClaudeAgentOptions, ProcessError
170
171
try:
172
options = ClaudeAgentOptions(
173
allowed_tools=["Bash"],
174
permission_mode="bypassPermissions"
175
)
176
async for msg in query(prompt="Run invalid command", options=options):
177
print(msg)
178
except ProcessError as e:
179
print(f"Process failed: {e}")
180
if e.exit_code:
181
print(f"Exit code: {e.exit_code}")
182
if e.stderr:
183
print(f"Error output: {e.stderr}")
184
```
185
186
**Common Exit Codes:**
187
188
- `1`: General error
189
- `2`: Configuration error
190
- `126`: Permission denied
191
- `127`: Command not found
192
- `130`: Interrupted (Ctrl+C)
193
194
**Handling Specific Exit Codes:**
195
196
```python
197
from claude_agent_sdk import query, ProcessError
198
199
try:
200
async for msg in query(prompt="Hello"):
201
print(msg)
202
except ProcessError as e:
203
if e.exit_code == 126:
204
print("Permission denied - check file/directory permissions")
205
elif e.exit_code == 130:
206
print("Operation interrupted by user")
207
else:
208
print(f"Process failed with exit code {e.exit_code}")
209
```
210
211
### Parsing Errors
212
213
#### CLIJSONDecodeError
214
215
Raised when unable to decode JSON from CLI output.
216
217
```python { .api }
218
class CLIJSONDecodeError(ClaudeSDKError):
219
"""Raised when unable to decode JSON from CLI output."""
220
221
def __init__(self, line: str, original_error: Exception):
222
"""
223
Initialize CLIJSONDecodeError.
224
225
Args:
226
line: Line that failed to parse
227
original_error: Original JSON decode error
228
"""
229
230
line: str
231
original_error: Exception
232
```
233
234
**Attributes:**
235
236
- `line` (str): The line of text that failed to parse as JSON.
237
- `original_error` (Exception): The original JSON decode error from Python's json module.
238
239
**Common Causes:**
240
241
- CLI output format changed (version mismatch)
242
- Corrupted message data
243
- CLI stderr mixed with stdout
244
- Binary data in message stream
245
246
**Usage Example:**
247
248
```python
249
from claude_agent_sdk import query, CLIJSONDecodeError
250
251
try:
252
async for msg in query(prompt="Hello"):
253
print(msg)
254
except CLIJSONDecodeError as e:
255
print(f"Failed to parse CLI output: {e}")
256
print(f"Problematic line: {e.line[:100]}") # First 100 chars
257
print(f"Original error: {e.original_error}")
258
```
259
260
**Debugging JSON Parse Errors:**
261
262
```python
263
from claude_agent_sdk import query, ClaudeAgentOptions, CLIJSONDecodeError
264
265
def debug_stderr(line: str):
266
print(f"[DEBUG] CLI stderr: {line}")
267
268
try:
269
options = ClaudeAgentOptions(stderr=debug_stderr)
270
async for msg in query(prompt="Hello", options=options):
271
print(msg)
272
except CLIJSONDecodeError as e:
273
print(f"JSON parse error: {e}")
274
# Check if stderr callback revealed issues
275
```
276
277
#### MessageParseError
278
279
Raised when unable to parse a message from CLI output.
280
281
```python { .api }
282
class MessageParseError(ClaudeSDKError):
283
"""Raised when unable to parse a message from CLI output."""
284
285
def __init__(self, message: str, data: dict[str, Any] | None = None):
286
"""
287
Initialize MessageParseError.
288
289
Args:
290
message: Error message
291
data: Raw message data if available
292
"""
293
294
data: dict[str, Any] | None
295
```
296
297
**Attributes:**
298
299
- `data` (dict[str, Any] | None): Raw message data that failed to parse.
300
301
**Common Causes:**
302
303
- Unknown message type
304
- Missing required fields
305
- Invalid field types
306
- Schema version mismatch
307
308
**Usage Example:**
309
310
```python
311
from claude_agent_sdk import query, MessageParseError
312
313
try:
314
async for msg in query(prompt="Hello"):
315
print(msg)
316
except MessageParseError as e:
317
print(f"Failed to parse message: {e}")
318
if e.data:
319
print(f"Raw data: {e.data}")
320
```
321
322
## Error Handling Patterns
323
324
### Comprehensive Error Handling
325
326
```python
327
from claude_agent_sdk import (
328
query, ClaudeAgentOptions,
329
ClaudeSDKError, CLINotFoundError, CLIConnectionError,
330
ProcessError, CLIJSONDecodeError, MessageParseError
331
)
332
import anyio
333
334
async def safe_query(prompt: str, options: ClaudeAgentOptions | None = None):
335
"""Query with comprehensive error handling."""
336
try:
337
async for msg in query(prompt=prompt, options=options):
338
yield msg
339
340
except CLINotFoundError as e:
341
print(f"ERROR: Claude Code CLI not found: {e}")
342
print(" Please check installation or cli_path option")
343
344
except ProcessError as e:
345
print(f"ERROR: CLI process failed: {e}")
346
if e.exit_code:
347
print(f" Exit code: {e.exit_code}")
348
if e.stderr:
349
print(f" Error output: {e.stderr[:500]}") # First 500 chars
350
351
except CLIJSONDecodeError as e:
352
print(f"ERROR: Failed to parse CLI output: {e}")
353
print(f" Problematic line: {e.line[:200]}")
354
print(f" This may indicate a version mismatch")
355
356
except MessageParseError as e:
357
print(f"ERROR: Failed to parse message: {e}")
358
if e.data:
359
print(f" Message type: {e.data.get('type', 'unknown')}")
360
361
except CLIConnectionError as e:
362
print(f"ERROR: Failed to connect to Claude Code: {e}")
363
print(" Check that Claude Code is properly installed")
364
365
except ClaudeSDKError as e:
366
print(f"ERROR: SDK error: {e}")
367
368
except Exception as e:
369
print(f"ERROR: Unexpected error: {type(e).__name__}: {e}")
370
371
# Usage
372
async def main():
373
async for msg in safe_query("Hello, Claude!"):
374
print(msg)
375
376
anyio.run(main)
377
```
378
379
### Retry Logic
380
381
```python
382
from claude_agent_sdk import query, ProcessError, CLIConnectionError
383
import anyio
384
385
async def query_with_retry(
386
prompt: str,
387
max_retries: int = 3,
388
retry_delay: float = 1.0
389
):
390
"""Query with automatic retry on transient errors."""
391
for attempt in range(max_retries):
392
try:
393
async for msg in query(prompt=prompt):
394
yield msg
395
return # Success
396
397
except (ProcessError, CLIConnectionError) as e:
398
if attempt < max_retries - 1:
399
print(f"Attempt {attempt + 1} failed: {e}")
400
print(f"Retrying in {retry_delay} seconds...")
401
await anyio.sleep(retry_delay)
402
retry_delay *= 2 # Exponential backoff
403
else:
404
print(f"All {max_retries} attempts failed")
405
raise
406
407
# Usage
408
async def main():
409
async for msg in query_with_retry("Hello", max_retries=3):
410
print(msg)
411
```
412
413
### Graceful Degradation
414
415
```python
416
from claude_agent_sdk import (
417
query, ClaudeAgentOptions, CLINotFoundError, ProcessError
418
)
419
420
async def query_with_fallback(prompt: str):
421
"""Query with fallback to simpler configuration."""
422
# Try with full configuration
423
try:
424
options = ClaudeAgentOptions(
425
allowed_tools=["Read", "Write", "Bash"],
426
permission_mode="acceptEdits",
427
model="claude-opus-4-1-20250805"
428
)
429
async for msg in query(prompt=prompt, options=options):
430
yield msg
431
return
432
433
except ProcessError as e:
434
print(f"Full config failed: {e}, trying with reduced tools...")
435
436
# Fallback to fewer tools
437
try:
438
options = ClaudeAgentOptions(
439
allowed_tools=["Read"],
440
model="claude-sonnet-4-5-20250929"
441
)
442
async for msg in query(prompt=prompt, options=options):
443
yield msg
444
return
445
446
except ProcessError as e:
447
print(f"Reduced tools failed: {e}, trying basic query...")
448
449
# Fallback to basic query
450
async for msg in query(prompt=prompt):
451
yield msg
452
```
453
454
### Error Context Collection
455
456
```python
457
from claude_agent_sdk import query, ClaudeAgentOptions, ClaudeSDKError
458
import traceback
459
import sys
460
461
async def query_with_context(prompt: str, options: ClaudeAgentOptions | None = None):
462
"""Query with detailed error context collection."""
463
try:
464
async for msg in query(prompt=prompt, options=options):
465
yield msg
466
467
except ClaudeSDKError as e:
468
# Collect error context
469
error_info = {
470
"error_type": type(e).__name__,
471
"error_message": str(e),
472
"traceback": traceback.format_exc(),
473
"prompt": prompt[:100], # First 100 chars
474
"python_version": sys.version,
475
}
476
477
# Add error-specific context
478
if hasattr(e, "exit_code"):
479
error_info["exit_code"] = e.exit_code
480
if hasattr(e, "stderr"):
481
error_info["stderr"] = e.stderr[:500] if e.stderr else None
482
if hasattr(e, "cli_path"):
483
error_info["cli_path"] = e.cli_path
484
if hasattr(e, "data"):
485
error_info["raw_data"] = e.data
486
487
# Log or report error context
488
print("Error context:")
489
for key, value in error_info.items():
490
print(f" {key}: {value}")
491
492
raise
493
```
494
495
### User-Friendly Error Messages
496
497
```python
498
from claude_agent_sdk import (
499
query, ClaudeAgentOptions,
500
CLINotFoundError, ProcessError, CLIConnectionError
501
)
502
503
async def user_friendly_query(prompt: str):
504
"""Query with user-friendly error messages."""
505
try:
506
async for msg in query(prompt=prompt):
507
yield msg
508
509
except CLINotFoundError:
510
print("""
511
╭─────────────────────────────────────────────╮
512
│ Claude Code Not Found │
513
├─────────────────────────────────────────────┤
514
│ The Claude Code CLI could not be found. │
515
│ │
516
│ Please ensure: │
517
│ • Claude Code is properly installed │
518
│ • The bundled CLI is not corrupted │
519
│ • You have the correct cli_path configured │
520
│ │
521
│ Visit: https://docs.anthropic.com/claude │
522
╰─────────────────────────────────────────────╯
523
""")
524
525
except ProcessError as e:
526
if e.exit_code == 126:
527
print("""
528
╭─────────────────────────────────────────────╮
529
│ Permission Denied │
530
├─────────────────────────────────────────────┤
531
│ The CLI process encountered a permission │
532
│ error. │
533
│ │
534
│ This may be caused by: │
535
│ • File/directory access restrictions │
536
│ • Tool permission mode settings │
537
│ • Operating system security policies │
538
│ │
539
│ Try adjusting permission_mode or working │
540
│ directory settings. │
541
╰─────────────────────────────────────────────╯
542
""")
543
else:
544
print(f"""
545
╭─────────────────────────────────────────────╮
546
│ Process Error │
547
├─────────────────────────────────────────────┤
548
│ The Claude Code CLI process failed. │
549
│ │
550
│ Exit code: {e.exit_code or 'Unknown'}
551
│ │
552
│ Error output: │
553
│ {(e.stderr or 'No error output')[:200]}
554
╰─────────────────────────────────────────────╯
555
""")
556
557
except CLIConnectionError:
558
print("""
559
╭─────────────────────────────────────────────╮
560
│ Connection Error │
561
├─────────────────────────────────────────────┤
562
│ Failed to establish connection to Claude │
563
│ Code CLI. │
564
│ │
565
│ This may indicate: │
566
│ • CLI initialization failure │
567
│ • Invalid configuration options │
568
│ • Resource constraints │
569
│ │
570
│ Check logs for more details. │
571
╰─────────────────────────────────────────────╯
572
""")
573
```
574
575
### Async Context Error Handling
576
577
```python
578
from claude_agent_sdk import ClaudeSDKClient, ClaudeSDKError
579
import anyio
580
581
async def safe_client_session(prompt: str):
582
"""Client session with proper error handling."""
583
client = None
584
try:
585
client = ClaudeSDKClient()
586
await client.connect()
587
588
await client.query(prompt)
589
async for msg in client.receive_response():
590
print(msg)
591
592
except ClaudeSDKError as e:
593
print(f"Error during session: {e}")
594
# Handle error appropriately
595
596
finally:
597
# Always clean up
598
if client:
599
try:
600
await client.disconnect()
601
except Exception as e:
602
print(f"Error during cleanup: {e}")
603
604
# Better: use context manager
605
async def safe_client_session_v2(prompt: str):
606
"""Client session with context manager."""
607
try:
608
async with ClaudeSDKClient() as client:
609
await client.query(prompt)
610
async for msg in client.receive_response():
611
print(msg)
612
except ClaudeSDKError as e:
613
print(f"Error during session: {e}")
614
```
615
616
## Best Practices
617
618
1. **Catch Specific Exceptions**: Catch specific exception types rather than broad `Exception`
619
620
2. **Log Error Details**: Log error attributes (exit_code, stderr, etc.) for debugging
621
622
3. **Provide Context**: Include relevant context (prompt, options) in error logs
623
624
4. **User-Friendly Messages**: Show clear, actionable error messages to users
625
626
5. **Cleanup Resources**: Use context managers or finally blocks for cleanup
627
628
6. **Retry Transient Errors**: Implement retry logic for network/process failures
629
630
7. **Graceful Degradation**: Fall back to simpler configurations when possible
631
632
8. **Error Reporting**: Collect and report error context for bug fixes
633
634
9. **Version Checking**: Ensure SDK and CLI versions are compatible
635
636
10. **Documentation**: Document error handling patterns for your team
637
638
## Testing Error Handling
639
640
```python
641
from claude_agent_sdk import (
642
query, ClaudeAgentOptions, ClaudeSDKError
643
)
644
import pytest
645
646
@pytest.mark.asyncio
647
async def test_invalid_cli_path():
648
"""Test handling of invalid CLI path."""
649
with pytest.raises(CLINotFoundError):
650
options = ClaudeAgentOptions(cli_path="/nonexistent/path")
651
async for msg in query(prompt="Hello", options=options):
652
pass
653
654
@pytest.mark.asyncio
655
async def test_error_recovery():
656
"""Test error recovery and retry."""
657
attempts = 0
658
max_attempts = 3
659
660
while attempts < max_attempts:
661
try:
662
async for msg in query(prompt="Hello"):
663
break # Success
664
except ClaudeSDKError:
665
attempts += 1
666
if attempts >= max_attempts:
667
raise
668
669
assert attempts < max_attempts, "Should succeed within retry limit"
670
```
671
672
## Debugging Tips
673
674
### Enable Debug Logging
675
676
```python
677
import logging
678
from claude_agent_sdk import ClaudeAgentOptions
679
680
logging.basicConfig(level=logging.DEBUG)
681
682
def stderr_handler(line: str):
683
logging.debug(f"CLI stderr: {line}")
684
685
options = ClaudeAgentOptions(stderr=stderr_handler)
686
```
687
688
### Inspect Error Attributes
689
690
```python
691
from claude_agent_sdk import query, ClaudeSDKError
692
693
try:
694
async for msg in query(prompt="Hello"):
695
print(msg)
696
except ClaudeSDKError as e:
697
# Inspect all error attributes
698
print(f"Error type: {type(e)}")
699
print(f"Error message: {e}")
700
print(f"Attributes: {dir(e)}")
701
702
# Error-specific attributes
703
if hasattr(e, "__dict__"):
704
print(f"Instance dict: {e.__dict__}")
705
```
706
707
### Check SDK Version
708
709
```python
710
from claude_agent_sdk import __version__
711
712
print(f"SDK version: {__version__}")
713
```
714
715
### Validate Configuration
716
717
```python
718
from claude_agent_sdk import ClaudeAgentOptions
719
720
options = ClaudeAgentOptions(
721
allowed_tools=["Read", "Write"],
722
permission_mode="acceptEdits"
723
)
724
725
# Validate before use
726
assert options.permission_mode in ["default", "acceptEdits", "plan", "bypassPermissions"]
727
assert all(isinstance(tool, str) for tool in options.allowed_tools)
728
```
729