or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

agent-definitions.mdagents.mdclient.mdconfiguration-options.mdcontent-blocks.mdcore-query-interface.mdcustom-tools.mderror-handling.mderrors.mdhook-system.mdhooks.mdindex.mdmcp-config.mdmcp-server-configuration.mdmessages-and-content.mdmessages.mdoptions.mdpermission-control.mdpermissions.mdquery.mdtransport.md
COMPLETION_SUMMARY.md

error-handling.mddocs/

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