or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration-options.mdcustom-tools.mderror-handling.mdhook-system.mdindex.mdinteractive-client.mdmessage-types.mdsimple-queries.mdtransport-system.md

error-handling.mddocs/

0

# Error Handling

1

2

Comprehensive error handling with specific exception types for different failure modes. The Claude Code SDK provides detailed exception hierarchy for connection errors, process failures, JSON parsing issues, and message parsing errors.

3

4

## Capabilities

5

6

### Base Exception Class

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

### Connection Errors

16

17

Errors related to connecting to Claude Code CLI.

18

19

```python { .api }

20

class CLIConnectionError(ClaudeSDKError):

21

"""Raised when unable to connect to Claude Code."""

22

23

class CLINotFoundError(CLIConnectionError):

24

"""Raised when Claude Code is not found or not installed."""

25

26

def __init__(

27

self, message: str = "Claude Code not found", cli_path: str | None = None

28

):

29

"""

30

Initialize CLINotFoundError.

31

32

Args:

33

message: Error message

34

cli_path: Path where Claude Code was expected (if known)

35

"""

36

```

37

38

### Process Errors

39

40

Errors related to CLI process execution and failures.

41

42

```python { .api }

43

class ProcessError(ClaudeSDKError):

44

"""Raised when the CLI process fails."""

45

46

def __init__(

47

self, message: str, exit_code: int | None = None, stderr: str | None = None

48

):

49

"""

50

Initialize ProcessError.

51

52

Args:

53

message: Error description

54

exit_code: Process exit code (if available)

55

stderr: Standard error output (if available)

56

"""

57

58

exit_code: int | None

59

stderr: str | None

60

```

61

62

### JSON and Message Parsing Errors

63

64

Errors related to parsing Claude Code CLI output and responses.

65

66

```python { .api }

67

class CLIJSONDecodeError(ClaudeSDKError):

68

"""Raised when unable to decode JSON from CLI output."""

69

70

def __init__(self, line: str, original_error: Exception):

71

"""

72

Initialize CLIJSONDecodeError.

73

74

Args:

75

line: The line that failed to parse

76

original_error: The original JSON decode exception

77

"""

78

79

line: str

80

original_error: Exception

81

```

82

83

## Usage Examples

84

85

### Basic Error Handling

86

87

```python

88

from claude_code_sdk import (

89

query, ClaudeSDKError, CLINotFoundError,

90

CLIConnectionError, ProcessError, CLIJSONDecodeError

91

)

92

93

async def main():

94

try:

95

async for message in query(prompt="Hello Claude"):

96

print(message)

97

98

except CLINotFoundError:

99

print("Error: Claude Code is not installed.")

100

print("Please install with: npm install -g @anthropic-ai/claude-code")

101

102

except CLIConnectionError as e:

103

print(f"Error: Unable to connect to Claude Code: {e}")

104

105

except ProcessError as e:

106

print(f"Error: Claude Code process failed: {e}")

107

if e.exit_code:

108

print(f"Exit code: {e.exit_code}")

109

if e.stderr:

110

print(f"Error output: {e.stderr}")

111

112

except CLIJSONDecodeError as e:

113

print(f"Error: Failed to parse Claude Code response")

114

print(f"Problematic line: {e.line[:100]}")

115

print(f"Original error: {e.original_error}")

116

117

except ClaudeSDKError as e:

118

print(f"Error: General Claude SDK error: {e}")

119

120

except Exception as e:

121

print(f"Unexpected error: {e}")

122

```

123

124

### Specific Error Handling with Recovery

125

126

```python

127

import asyncio

128

import time

129

from claude_code_sdk import (

130

query, ClaudeCodeOptions, CLINotFoundError,

131

CLIConnectionError, ProcessError

132

)

133

134

async def robust_query(prompt: str, max_retries: int = 3) -> list:

135

"""Query with retry logic and error handling."""

136

messages = []

137

last_error = None

138

139

for attempt in range(max_retries):

140

try:

141

async for message in query(prompt=prompt):

142

messages.append(message)

143

return messages

144

145

except CLINotFoundError as e:

146

print("Claude Code not found. Please install it first.")

147

raise e # Don't retry installation errors

148

149

except CLIConnectionError as e:

150

last_error = e

151

print(f"Connection error (attempt {attempt + 1}/{max_retries}): {e}")

152

if attempt < max_retries - 1:

153

await asyncio.sleep(2 ** attempt) # Exponential backoff

154

continue

155

156

except ProcessError as e:

157

last_error = e

158

# Retry on certain exit codes

159

if e.exit_code in [1, 2]: # Retryable errors

160

print(f"Process error (attempt {attempt + 1}/{max_retries}): {e}")

161

if attempt < max_retries - 1:

162

await asyncio.sleep(1)

163

continue

164

else:

165

# Non-retryable process error

166

raise e

167

168

# If we get here, all retries failed

169

raise last_error or Exception("All retry attempts failed")

170

171

async def main():

172

try:

173

messages = await robust_query("What is the capital of France?")

174

for message in messages:

175

print(message)

176

except Exception as e:

177

print(f"Failed after all retries: {e}")

178

```

179

180

### Client Error Handling

181

182

```python

183

from claude_code_sdk import (

184

ClaudeSDKClient, ClaudeCodeOptions,

185

CLIConnectionError, ProcessError

186

)

187

188

async def main():

189

client = ClaudeSDKClient()

190

191

try:

192

await client.connect("Hello Claude")

193

194

async for msg in client.receive_response():

195

print(msg)

196

197

except CLIConnectionError as e:

198

print(f"Failed to connect: {e}")

199

print("Ensure Claude Code is installed and accessible")

200

201

except ProcessError as e:

202

print(f"Process error during conversation: {e}")

203

if e.exit_code == 130: # SIGINT

204

print("Conversation was interrupted")

205

elif e.stderr and "permission" in e.stderr.lower():

206

print("Permission denied - check tool permissions")

207

208

finally:

209

try:

210

await client.disconnect()

211

except Exception as disconnect_error:

212

print(f"Error during disconnect: {disconnect_error}")

213

```

214

215

### Custom Tool Error Handling

216

217

```python

218

from claude_code_sdk import (

219

tool, create_sdk_mcp_server, ClaudeSDKClient,

220

ClaudeCodeOptions, ProcessError

221

)

222

223

@tool("divide", "Divide two numbers", {"a": float, "b": float})

224

async def divide_numbers(args):

225

"""Tool with proper error handling."""

226

try:

227

if args["b"] == 0:

228

return {

229

"content": [

230

{"type": "text", "text": "Error: Division by zero is not allowed"}

231

],

232

"is_error": True

233

}

234

235

result = args["a"] / args["b"]

236

return {

237

"content": [

238

{"type": "text", "text": f"Result: {result}"}

239

]

240

}

241

242

except Exception as e:

243

return {

244

"content": [

245

{"type": "text", "text": f"Unexpected error: {str(e)}"}

246

],

247

"is_error": True

248

}

249

250

async def main():

251

server = create_sdk_mcp_server("calculator", tools=[divide_numbers])

252

options = ClaudeCodeOptions(

253

mcp_servers={"calc": server},

254

allowed_tools=["mcp__calc__divide"]

255

)

256

257

try:

258

async with ClaudeSDKClient(options=options) as client:

259

await client.query("Divide 10 by 0")

260

261

async for msg in client.receive_response():

262

# Check for tool errors in messages

263

if hasattr(msg, 'content'):

264

for block in getattr(msg, 'content', []):

265

if hasattr(block, 'is_error') and block.is_error:

266

print(f"Tool reported error: {block.content}")

267

elif hasattr(block, 'text'):

268

print(f"Response: {block.text}")

269

270

except ProcessError as e:

271

print(f"Process error with custom tools: {e}")

272

```

273

274

### Logging and Debugging

275

276

```python

277

import logging

278

import sys

279

from claude_code_sdk import (

280

query, ClaudeCodeOptions, CLIJSONDecodeError,

281

MessageParseError

282

)

283

284

# Configure logging

285

logging.basicConfig(

286

level=logging.DEBUG,

287

format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',

288

handlers=[

289

logging.FileHandler('claude_sdk.log'),

290

logging.StreamHandler(sys.stdout)

291

]

292

)

293

294

logger = logging.getLogger('claude_sdk_app')

295

296

async def main():

297

# Enable debug output

298

debug_file = open("claude_debug.log", "w")

299

300

options = ClaudeCodeOptions(

301

debug_stderr=debug_file,

302

allowed_tools=["Read", "Write", "Bash"]

303

)

304

305

try:

306

async for message in query(

307

prompt="Create a complex project structure",

308

options=options

309

):

310

logger.info(f"Received message: {type(message).__name__}")

311

print(message)

312

313

except CLIJSONDecodeError as e:

314

logger.error(f"JSON decode error: {e.line[:100]}")

315

logger.error(f"Original error: {e.original_error}")

316

317

# Log the problematic line for debugging

318

with open("failed_json.log", "w") as f:

319

f.write(f"Failed line: {e.line}\n")

320

f.write(f"Error: {e.original_error}\n")

321

322

except MessageParseError as e:

323

logger.error(f"Message parse error: {e}")

324

if e.data:

325

logger.error(f"Problematic data: {e.data}")

326

327

except Exception as e:

328

logger.exception(f"Unexpected error: {e}")

329

330

finally:

331

debug_file.close()

332

```

333

334

### Context Manager Error Handling

335

336

```python

337

from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

338

from contextlib import asynccontextmanager

339

340

@asynccontextmanager

341

async def claude_client_with_recovery(options=None):

342

"""Context manager with automatic error recovery."""

343

client = ClaudeSDKClient(options or ClaudeCodeOptions())

344

345

try:

346

await client.connect()

347

yield client

348

349

except CLIConnectionError as e:

350

print(f"Connection error: {e}")

351

# Attempt recovery

352

try:

353

await client.connect()

354

yield client

355

except Exception as recovery_error:

356

print(f"Recovery failed: {recovery_error}")

357

raise e

358

359

except Exception as e:

360

print(f"Unexpected error in context manager: {e}")

361

raise

362

363

finally:

364

try:

365

await client.disconnect()

366

except Exception as disconnect_error:

367

print(f"Error during cleanup: {disconnect_error}")

368

369

async def main():

370

try:

371

async with claude_client_with_recovery() as client:

372

await client.query("Test with error recovery")

373

374

async for msg in client.receive_response():

375

print(msg)

376

377

except Exception as e:

378

print(f"Final error: {e}")

379

```

380

381

### Validation Error Handling

382

383

```python

384

from claude_code_sdk import ClaudeCodeOptions, ClaudeSDKClient

385

386

async def validate_options(options: ClaudeCodeOptions) -> ClaudeCodeOptions:

387

"""Validate and fix common configuration errors."""

388

# Fix common tool name issues

389

if options.allowed_tools:

390

fixed_tools = []

391

for tool in options.allowed_tools:

392

if tool in ["bash", "Bash"]:

393

fixed_tools.append("Bash")

394

elif tool in ["read", "Read"]:

395

fixed_tools.append("Read")

396

elif tool in ["write", "Write"]:

397

fixed_tools.append("Write")

398

else:

399

fixed_tools.append(tool)

400

401

options.allowed_tools = fixed_tools

402

403

# Check for conflicting permission settings

404

if options.can_use_tool and options.permission_prompt_tool_name:

405

print("Warning: can_use_tool and permission_prompt_tool_name are mutually exclusive")

406

print("Setting permission_prompt_tool_name to None")

407

options.permission_prompt_tool_name = None

408

409

return options

410

411

async def main():

412

try:

413

# Potentially problematic configuration

414

options = ClaudeCodeOptions(

415

allowed_tools=["bash", "read", "write"], # Wrong case

416

can_use_tool=lambda *args: None, # Invalid callback

417

permission_prompt_tool_name="custom" # Conflicting setting

418

)

419

420

# Validate and fix

421

options = await validate_options(options)

422

423

async with ClaudeSDKClient(options=options) as client:

424

await client.query("Test with validated options")

425

426

async for msg in client.receive_response():

427

print(msg)

428

429

except ValueError as e:

430

print(f"Configuration error: {e}")

431

except Exception as e:

432

print(f"Unexpected error: {e}")

433

```

434

435

## Error Categories and Recovery Strategies

436

437

### Installation Errors

438

439

- **Error**: `CLINotFoundError`

440

- **Cause**: Claude Code not installed

441

- **Recovery**: Install Claude Code, check PATH

442

- **Strategy**: Don't retry, show installation instructions

443

444

### Connection Errors

445

446

- **Error**: `CLIConnectionError`

447

- **Cause**: Can't connect to Claude Code process

448

- **Recovery**: Retry with backoff, check process permissions

449

- **Strategy**: Limited retries with exponential backoff

450

451

### Process Errors

452

453

- **Error**: `ProcessError`

454

- **Cause**: Claude Code CLI process failure

455

- **Recovery**: Depends on exit code

456

- **Strategy**: Retry for transient errors, fail for permanent errors

457

458

### Parsing Errors

459

460

- **Error**: `CLIJSONDecodeError`, `MessageParseError`

461

- **Cause**: Malformed output from Claude Code

462

- **Recovery**: Log problematic data, potentially retry

463

- **Strategy**: Log for debugging, limited retries

464

465

## Best Practices

466

467

### Error Handling Guidelines

468

469

1. **Catch specific exceptions** rather than generic `Exception`

470

2. **Provide user-friendly error messages** for common issues

471

3. **Log detailed error information** for debugging

472

4. **Implement appropriate retry logic** for transient errors

473

5. **Clean up resources** in finally blocks or context managers

474

475

### Debugging Support

476

477

1. **Enable debug output** with `debug_stderr` option

478

2. **Log all communication** for troubleshooting

479

3. **Preserve failed data** for error analysis

480

4. **Use structured logging** with appropriate levels

481

482

### Resource Management

483

484

1. **Always disconnect clients** in finally blocks

485

2. **Use context managers** for automatic cleanup

486

3. **Handle disconnect errors** gracefully

487

4. **Monitor for resource leaks** in long-running applications

488

489

For related configuration options, see [Configuration and Options](./configuration-options.md).

490

For transport-specific errors, see [Transport System](./transport-system.md).