or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-setup.mderrors.mdindex.mdmessages.mdplatform-adapters.mdstreaming.mdstructured-outputs.mdtools.md

errors.mddocs/

0

# Error Handling

1

2

The Anthropic Java SDK provides a comprehensive exception hierarchy for handling different types of errors that may occur during API interactions. All exceptions are unchecked (extending `RuntimeException`) to provide flexibility in error handling without forcing verbose exception declarations.

3

4

## Exception Hierarchy

5

6

All Anthropic SDK exceptions extend from the base class:

7

8

```java { .api }

9

package com.anthropic.errors;

10

11

public class AnthropicException extends RuntimeException {

12

public AnthropicException(String message);

13

public AnthropicException(String message, Throwable cause);

14

}

15

```

16

17

The exception hierarchy is organized into several categories:

18

19

```

20

AnthropicException (base)

21

├── AnthropicServiceException (HTTP errors)

22

│ ├── BadRequestException (400)

23

│ ├── UnauthorizedException (401)

24

│ ├── PermissionDeniedException (403)

25

│ ├── NotFoundException (404)

26

│ ├── UnprocessableEntityException (422)

27

│ ├── RateLimitException (429)

28

│ ├── InternalServerException (5xx)

29

│ ├── UnexpectedStatusCodeException (other status codes)

30

│ └── SseException (streaming errors)

31

├── AnthropicIoException (network/I/O errors)

32

├── AnthropicRetryableException (retryable failures)

33

└── AnthropicInvalidDataException (parsing/validation errors)

34

```

35

36

## Service Exceptions

37

38

Service exceptions represent HTTP errors returned by the Anthropic API. All service exceptions extend `AnthropicServiceException`:

39

40

```java { .api }

41

package com.anthropic.errors;

42

43

import com.anthropic.core.http.Headers;

44

45

public class AnthropicServiceException extends AnthropicException {

46

private final int statusCode;

47

private final Headers headers;

48

49

public int statusCode();

50

public Headers headers();

51

}

52

```

53

54

### BadRequestException (400)

55

56

Indicates invalid request parameters or malformed request data:

57

58

```java { .api }

59

package com.anthropic.errors;

60

61

public class BadRequestException extends AnthropicServiceException {

62

public BadRequestException(

63

int statusCode,

64

Headers headers,

65

String message,

66

Throwable cause

67

);

68

}

69

```

70

71

**Common causes:**

72

- Invalid parameter values

73

- Malformed JSON in request body

74

- Missing required parameters

75

- Parameters that conflict with each other

76

77

**Example:**

78

79

```java

80

import com.anthropic.errors.BadRequestException;

81

import com.anthropic.models.messages.MessageCreateParams;

82

import com.anthropic.models.messages.Model;

83

84

try {

85

MessageCreateParams params = MessageCreateParams.builder()

86

.maxTokens(0L) // Invalid: must be positive

87

.addUserMessage("Hello")

88

.model(Model.CLAUDE_SONNET_4_20250514)

89

.build();

90

91

client.messages().create(params);

92

} catch (BadRequestException e) {

93

System.err.println("Bad request: " + e.getMessage());

94

System.err.println("Status code: " + e.statusCode());

95

}

96

```

97

98

### UnauthorizedException (401)

99

100

Indicates authentication failure:

101

102

```java { .api }

103

package com.anthropic.errors;

104

105

public class UnauthorizedException extends AnthropicServiceException {

106

public UnauthorizedException(

107

int statusCode,

108

Headers headers,

109

String message,

110

Throwable cause

111

);

112

}

113

```

114

115

**Common causes:**

116

- Missing API key

117

- Invalid API key

118

- Expired authentication token

119

120

**Example:**

121

122

```java

123

import com.anthropic.errors.UnauthorizedException;

124

125

try {

126

Message message = client.messages().create(params);

127

} catch (UnauthorizedException e) {

128

System.err.println("Authentication failed: " + e.getMessage());

129

// Log for debugging, check API key configuration

130

}

131

```

132

133

### PermissionDeniedException (403)

134

135

Indicates insufficient permissions for the requested operation:

136

137

```java { .api }

138

package com.anthropic.errors;

139

140

public class PermissionDeniedException extends AnthropicServiceException {

141

public PermissionDeniedException(

142

int statusCode,

143

Headers headers,

144

String message,

145

Throwable cause

146

);

147

}

148

```

149

150

**Common causes:**

151

- API key lacks necessary permissions

152

- Access to specific model or feature denied

153

- Organization or account restrictions

154

155

**Example:**

156

157

```java

158

import com.anthropic.errors.PermissionDeniedException;

159

160

try {

161

Message message = client.messages().create(params);

162

} catch (PermissionDeniedException e) {

163

System.err.println("Permission denied: " + e.getMessage());

164

// Check account permissions and model access

165

}

166

```

167

168

### NotFoundException (404)

169

170

Indicates the requested resource does not exist:

171

172

```java { .api }

173

package com.anthropic.errors;

174

175

public class NotFoundException extends AnthropicServiceException {

176

public NotFoundException(

177

int statusCode,

178

Headers headers,

179

String message,

180

Throwable cause

181

);

182

}

183

```

184

185

**Common causes:**

186

- Invalid resource ID

187

- Resource has been deleted

188

- Incorrect endpoint path

189

190

**Example:**

191

192

```java

193

import com.anthropic.errors.NotFoundException;

194

195

try {

196

MessageBatch batch = client.messages().batches().retrieve("invalid-id");

197

} catch (NotFoundException e) {

198

System.err.println("Resource not found: " + e.getMessage());

199

// Handle missing resource gracefully

200

}

201

```

202

203

### UnprocessableEntityException (422)

204

205

Indicates the request was well-formed but contains semantic errors:

206

207

```java { .api }

208

package com.anthropic.errors;

209

210

public class UnprocessableEntityException extends AnthropicServiceException {

211

public UnprocessableEntityException(

212

int statusCode,

213

Headers headers,

214

String message,

215

Throwable cause

216

);

217

}

218

```

219

220

**Common causes:**

221

- Invalid tool definitions

222

- Malformed JSON schema

223

- Invalid model parameters

224

- Business logic validation failures

225

226

**Example:**

227

228

```java

229

import com.anthropic.errors.UnprocessableEntityException;

230

231

try {

232

Message message = client.messages().create(params);

233

} catch (UnprocessableEntityException e) {

234

System.err.println("Unprocessable entity: " + e.getMessage());

235

// Review request parameters and tool definitions

236

}

237

```

238

239

### RateLimitException (429)

240

241

Indicates rate limit has been exceeded:

242

243

```java { .api }

244

package com.anthropic.errors;

245

246

public class RateLimitException extends AnthropicServiceException {

247

public RateLimitException(

248

int statusCode,

249

Headers headers,

250

String message,

251

Throwable cause

252

);

253

}

254

```

255

256

**Common causes:**

257

- Too many requests in a time window

258

- Token usage limits exceeded

259

- Concurrent request limits exceeded

260

261

**Example:**

262

263

```java

264

import com.anthropic.errors.RateLimitException;

265

import java.util.Optional;

266

267

try {

268

Message message = client.messages().create(params);

269

} catch (RateLimitException e) {

270

System.err.println("Rate limit exceeded: " + e.getMessage());

271

272

// Check for retry-after header

273

Optional<String> retryAfter = e.headers().get("retry-after").stream().findFirst();

274

if (retryAfter.isPresent()) {

275

int seconds = Integer.parseInt(retryAfter.get());

276

System.out.println("Retry after " + seconds + " seconds");

277

}

278

}

279

```

280

281

### InternalServerException (5xx)

282

283

Indicates a server-side error:

284

285

```java { .api }

286

package com.anthropic.errors;

287

288

public class InternalServerException extends AnthropicServiceException {

289

public InternalServerException(

290

int statusCode,

291

Headers headers,

292

String message,

293

Throwable cause

294

);

295

}

296

```

297

298

**Common causes:**

299

- Temporary server issues

300

- Service overload

301

- Internal server errors

302

303

**Example:**

304

305

```java

306

import com.anthropic.errors.InternalServerException;

307

308

try {

309

Message message = client.messages().create(params);

310

} catch (InternalServerException e) {

311

System.err.println("Server error: " + e.getMessage());

312

System.err.println("Status code: " + e.statusCode());

313

// Implement retry logic with exponential backoff

314

}

315

```

316

317

### UnexpectedStatusCodeException

318

319

Handles HTTP status codes not covered by specific exception types:

320

321

```java { .api }

322

package com.anthropic.errors;

323

324

public class UnexpectedStatusCodeException extends AnthropicServiceException {

325

public UnexpectedStatusCodeException(

326

int statusCode,

327

Headers headers,

328

String message,

329

Throwable cause

330

);

331

}

332

```

333

334

**Example:**

335

336

```java

337

import com.anthropic.errors.UnexpectedStatusCodeException;

338

339

try {

340

Message message = client.messages().create(params);

341

} catch (UnexpectedStatusCodeException e) {

342

System.err.println("Unexpected status code: " + e.statusCode());

343

System.err.println("Message: " + e.getMessage());

344

}

345

```

346

347

## Streaming Exceptions

348

349

### SseException

350

351

Thrown for errors that occur during Server-Sent Events (SSE) streaming after a successful initial HTTP response:

352

353

```java { .api }

354

package com.anthropic.errors;

355

356

public class SseException extends AnthropicServiceException {

357

public SseException(

358

int statusCode,

359

Headers headers,

360

String message,

361

Throwable cause

362

);

363

}

364

```

365

366

**Common causes:**

367

- Connection interruption during streaming

368

- Malformed SSE data

369

- Server-side streaming errors

370

- Network issues mid-stream

371

372

**Example:**

373

374

```java

375

import com.anthropic.core.http.StreamResponse;

376

import com.anthropic.errors.SseException;

377

import com.anthropic.models.messages.RawMessageStreamEvent;

378

379

try (StreamResponse<RawMessageStreamEvent> stream =

380

client.messages().createStreaming(params)) {

381

382

stream.stream().forEach(event -> {

383

// Process streaming events

384

System.out.println(event);

385

});

386

} catch (SseException e) {

387

System.err.println("Streaming error: " + e.getMessage());

388

// Handle interrupted stream, may need to retry

389

}

390

```

391

392

## I/O Exceptions

393

394

### AnthropicIoException

395

396

Represents network and I/O errors during communication with the API:

397

398

```java { .api }

399

package com.anthropic.errors;

400

401

public class AnthropicIoException extends AnthropicException {

402

public AnthropicIoException(String message);

403

public AnthropicIoException(String message, Throwable cause);

404

}

405

```

406

407

**Common causes:**

408

- Network connectivity problems

409

- DNS resolution failures

410

- Connection timeouts

411

- SSL/TLS handshake errors

412

- Socket errors

413

414

**Example:**

415

416

```java

417

import com.anthropic.errors.AnthropicIoException;

418

419

try {

420

Message message = client.messages().create(params);

421

} catch (AnthropicIoException e) {

422

System.err.println("I/O error: " + e.getMessage());

423

// Check network connectivity

424

// Verify proxy settings

425

// Consider retry with backoff

426

}

427

```

428

429

## Retryable Exceptions

430

431

### AnthropicRetryableException

432

433

Indicates a failure that could be retried by the client:

434

435

```java { .api }

436

package com.anthropic.errors;

437

438

public class AnthropicRetryableException extends AnthropicException {

439

public AnthropicRetryableException(String message);

440

public AnthropicRetryableException(String message, Throwable cause);

441

}

442

```

443

444

**When thrown:**

445

- Generic retryable failures

446

- Transient errors not covered by specific exception types

447

448

**Example:**

449

450

```java

451

import com.anthropic.errors.AnthropicRetryableException;

452

453

try {

454

Message message = client.messages().create(params);

455

} catch (AnthropicRetryableException e) {

456

System.err.println("Retryable error: " + e.getMessage());

457

// Implement custom retry logic

458

}

459

```

460

461

## Invalid Data Exceptions

462

463

### AnthropicInvalidDataException

464

465

Thrown when successfully parsed data cannot be interpreted correctly:

466

467

```java { .api }

468

package com.anthropic.errors;

469

470

public class AnthropicInvalidDataException extends AnthropicException {

471

public AnthropicInvalidDataException(String message);

472

public AnthropicInvalidDataException(String message, Throwable cause);

473

}

474

```

475

476

**Common causes:**

477

- Missing required properties in API response

478

- Unexpected data types in response

479

- Invalid enum values

480

- Schema validation failures

481

- Response structure doesn't match SDK expectations

482

483

**Example:**

484

485

```java

486

import com.anthropic.errors.AnthropicInvalidDataException;

487

488

try {

489

Message message = client.messages().create(params);

490

491

// Accessing required property that API unexpectedly omitted

492

String id = message.id();

493

} catch (AnthropicInvalidDataException e) {

494

System.err.println("Invalid data: " + e.getMessage());

495

// API returned unexpected data format

496

// May need SDK update

497

}

498

```

499

500

## Error Properties

501

502

All service exceptions provide access to response metadata:

503

504

### Status Code

505

506

```java

507

import com.anthropic.errors.AnthropicServiceException;

508

509

try {

510

client.messages().create(params);

511

} catch (AnthropicServiceException e) {

512

int statusCode = e.statusCode();

513

System.err.println("HTTP status: " + statusCode);

514

}

515

```

516

517

### Headers

518

519

Access response headers for additional error context:

520

521

```java

522

import com.anthropic.core.http.Headers;

523

import com.anthropic.errors.AnthropicServiceException;

524

525

try {

526

client.messages().create(params);

527

} catch (AnthropicServiceException e) {

528

Headers headers = e.headers();

529

530

// Get specific header

531

headers.get("x-error-type").stream()

532

.findFirst()

533

.ifPresent(type -> System.err.println("Error type: " + type));

534

535

// Iterate all headers

536

headers.names().forEach(name -> {

537

headers.get(name).forEach(value -> {

538

System.err.println(name + ": " + value);

539

});

540

});

541

}

542

```

543

544

## Request IDs

545

546

Request IDs are crucial for debugging. They can be obtained from raw responses or error headers:

547

548

### From Raw Responses

549

550

```java

551

import com.anthropic.core.http.HttpResponseFor;

552

import com.anthropic.models.messages.Message;

553

import java.util.Optional;

554

555

HttpResponseFor<Message> response = client.messages()

556

.withRawResponse()

557

.create(params);

558

559

Optional<String> requestId = response.requestId();

560

requestId.ifPresent(id ->

561

System.out.println("Request ID: " + id)

562

);

563

```

564

565

### From Exception Headers

566

567

```java

568

import com.anthropic.errors.AnthropicServiceException;

569

import java.util.Optional;

570

571

try {

572

client.messages().create(params);

573

} catch (AnthropicServiceException e) {

574

Optional<String> requestId = e.headers()

575

.get("request-id")

576

.stream()

577

.findFirst();

578

579

if (requestId.isPresent()) {

580

System.err.println("Request ID for debugging: " + requestId.get());

581

// Include this in bug reports to Anthropic

582

}

583

}

584

```

585

586

## Retry Behavior

587

588

The SDK automatically retries certain errors with exponential backoff:

589

590

### Automatically Retried Errors

591

592

The following error types trigger automatic retries (default: 2 retries):

593

594

- **Connection errors** - Network connectivity problems

595

- **408 Request Timeout** - Server request timeout

596

- **409 Conflict** - Resource conflict

597

- **429 Rate Limit** - Rate limit exceeded

598

- **5xx Internal Server** - Server-side errors

599

600

### Configuring Retries

601

602

Set custom retry behavior at the client level:

603

604

```java

605

import com.anthropic.client.AnthropicClient;

606

import com.anthropic.client.okhttp.AnthropicOkHttpClient;

607

608

AnthropicClient client = AnthropicOkHttpClient.builder()

609

.fromEnv()

610

.maxRetries(4) // Increase from default 2

611

.build();

612

```

613

614

Disable retries:

615

616

```java

617

AnthropicClient client = AnthropicOkHttpClient.builder()

618

.fromEnv()

619

.maxRetries(0) // Disable automatic retries

620

.build();

621

```

622

623

### Non-Retried Errors

624

625

These errors are NOT automatically retried:

626

627

- **400 Bad Request** - Fix request parameters

628

- **401 Unauthorized** - Fix authentication

629

- **403 Permission Denied** - Fix permissions

630

- **404 Not Found** - Resource doesn't exist

631

- **422 Unprocessable Entity** - Fix request semantics

632

633

## Error Handling Patterns

634

635

### Basic Try-Catch

636

637

```java

638

import com.anthropic.errors.AnthropicException;

639

import com.anthropic.models.messages.Message;

640

641

try {

642

Message message = client.messages().create(params);

643

System.out.println("Success: " + message.id());

644

} catch (AnthropicException e) {

645

System.err.println("Error: " + e.getMessage());

646

e.printStackTrace();

647

}

648

```

649

650

### Specific Exception Handling

651

652

```java

653

import com.anthropic.errors.*;

654

655

try {

656

Message message = client.messages().create(params);

657

processMessage(message);

658

659

} catch (UnauthorizedException e) {

660

System.err.println("Authentication failed. Check API key.");

661

// Notify administrator

662

663

} catch (RateLimitException e) {

664

System.err.println("Rate limit exceeded. Backing off...");

665

// Implement backoff strategy

666

667

} catch (InternalServerException e) {

668

System.err.println("Server error. Will retry.");

669

// Implement retry logic

670

671

} catch (AnthropicIoException e) {

672

System.err.println("Network error: " + e.getMessage());

673

// Check connectivity

674

675

} catch (AnthropicException e) {

676

System.err.println("Unexpected error: " + e.getMessage());

677

// Log for investigation

678

}

679

```

680

681

### Async Error Handling

682

683

```java

684

import com.anthropic.errors.AnthropicException;

685

import java.util.concurrent.CompletableFuture;

686

687

CompletableFuture<Message> future = client.async().messages().create(params);

688

689

future.handle((message, error) -> {

690

if (error != null) {

691

if (error instanceof RateLimitException) {

692

System.err.println("Rate limited");

693

} else if (error instanceof AnthropicIoException) {

694

System.err.println("Network error");

695

} else {

696

System.err.println("Error: " + error.getMessage());

697

}

698

return null;

699

}

700

701

// Process successful message

702

System.out.println("Success: " + message.id());

703

return message;

704

});

705

```

706

707

### Streaming Error Handling

708

709

```java

710

import com.anthropic.core.http.StreamResponse;

711

import com.anthropic.errors.SseException;

712

import com.anthropic.models.messages.RawMessageStreamEvent;

713

714

try (StreamResponse<RawMessageStreamEvent> stream =

715

client.messages().createStreaming(params)) {

716

717

stream.stream().forEach(event -> {

718

try {

719

processEvent(event);

720

} catch (Exception e) {

721

System.err.println("Error processing event: " + e.getMessage());

722

// Continue processing other events

723

}

724

});

725

726

} catch (SseException e) {

727

System.err.println("Streaming interrupted: " + e.getMessage());

728

// Attempt to resume or restart stream

729

730

} catch (AnthropicException e) {

731

System.err.println("Error during streaming: " + e.getMessage());

732

}

733

```

734

735

### Retry with Exponential Backoff

736

737

```java

738

import com.anthropic.errors.*;

739

import com.anthropic.models.messages.Message;

740

import java.time.Duration;

741

742

public Message createWithRetry(MessageCreateParams params, int maxAttempts) {

743

int attempt = 0;

744

long backoffMs = 1000; // Start with 1 second

745

746

while (attempt < maxAttempts) {

747

try {

748

return client.messages().create(params);

749

750

} catch (RateLimitException e) {

751

attempt++;

752

if (attempt >= maxAttempts) {

753

throw e;

754

}

755

756

System.err.println("Rate limited, retrying in " + backoffMs + "ms");

757

sleep(backoffMs);

758

backoffMs *= 2; // Exponential backoff

759

760

} catch (InternalServerException e) {

761

attempt++;

762

if (attempt >= maxAttempts) {

763

throw e;

764

}

765

766

System.err.println("Server error, retrying in " + backoffMs + "ms");

767

sleep(backoffMs);

768

backoffMs *= 2;

769

770

} catch (AnthropicIoException e) {

771

attempt++;

772

if (attempt >= maxAttempts) {

773

throw e;

774

}

775

776

System.err.println("Network error, retrying in " + backoffMs + "ms");

777

sleep(backoffMs);

778

backoffMs *= 2;

779

}

780

}

781

782

throw new RuntimeException("Max retry attempts exceeded");

783

}

784

785

private void sleep(long ms) {

786

try {

787

Thread.sleep(ms);

788

} catch (InterruptedException e) {

789

Thread.currentThread().interrupt();

790

throw new RuntimeException(e);

791

}

792

}

793

```

794

795

### Circuit Breaker Pattern

796

797

```java

798

import com.anthropic.errors.*;

799

import com.anthropic.models.messages.Message;

800

import java.time.Instant;

801

import java.util.concurrent.atomic.AtomicInteger;

802

803

public class CircuitBreaker {

804

private final int failureThreshold;

805

private final long cooldownMs;

806

private final AtomicInteger failures = new AtomicInteger(0);

807

private volatile Instant lastFailureTime;

808

private volatile boolean open = false;

809

810

public CircuitBreaker(int failureThreshold, long cooldownMs) {

811

this.failureThreshold = failureThreshold;

812

this.cooldownMs = cooldownMs;

813

}

814

815

public Message create(MessageCreateParams params) {

816

if (open) {

817

if (Instant.now().toEpochMilli() - lastFailureTime.toEpochMilli()

818

> cooldownMs) {

819

// Try to close circuit

820

open = false;

821

failures.set(0);

822

} else {

823

throw new RuntimeException("Circuit breaker is open");

824

}

825

}

826

827

try {

828

Message message = client.messages().create(params);

829

failures.set(0); // Reset on success

830

return message;

831

832

} catch (RateLimitException | InternalServerException |

833

AnthropicIoException e) {

834

int count = failures.incrementAndGet();

835

lastFailureTime = Instant.now();

836

837

if (count >= failureThreshold) {

838

open = true;

839

System.err.println("Circuit breaker opened after " + count +

840

" failures");

841

}

842

throw e;

843

}

844

}

845

}

846

```

847

848

## Best Practices

849

850

### 1. Log Request IDs

851

852

Always log request IDs for failed requests:

853

854

```java

855

import com.anthropic.errors.AnthropicServiceException;

856

857

try {

858

client.messages().create(params);

859

} catch (AnthropicServiceException e) {

860

String requestId = e.headers().get("request-id")

861

.stream().findFirst().orElse("unknown");

862

863

System.err.println("Request failed: " + e.getMessage());

864

System.err.println("Request ID: " + requestId);

865

System.err.println("Status code: " + e.statusCode());

866

867

// Log to monitoring system

868

logger.error("Anthropic API error",

869

"requestId", requestId,

870

"statusCode", e.statusCode(),

871

"message", e.getMessage());

872

}

873

```

874

875

### 2. Implement Monitoring

876

877

Track error rates and types:

878

879

```java

880

import com.anthropic.errors.*;

881

import java.util.concurrent.ConcurrentHashMap;

882

import java.util.concurrent.atomic.AtomicLong;

883

884

public class ErrorMonitor {

885

private final ConcurrentHashMap<String, AtomicLong> errorCounts

886

= new ConcurrentHashMap<>();

887

888

public void recordError(AnthropicException e) {

889

String errorType = e.getClass().getSimpleName();

890

errorCounts.computeIfAbsent(errorType, k -> new AtomicLong())

891

.incrementAndGet();

892

}

893

894

public void printStats() {

895

System.out.println("Error Statistics:");

896

errorCounts.forEach((type, count) -> {

897

System.out.println(type + ": " + count.get());

898

});

899

}

900

}

901

```

902

903

### 3. Handle Rate Limits Gracefully

904

905

```java

906

import com.anthropic.errors.RateLimitException;

907

908

try {

909

return client.messages().create(params);

910

} catch (RateLimitException e) {

911

// Extract retry-after header

912

String retryAfter = e.headers().get("retry-after")

913

.stream().findFirst().orElse("60");

914

915

long waitSeconds = Long.parseLong(retryAfter);

916

System.out.println("Rate limited. Waiting " + waitSeconds + " seconds");

917

918

// Queue for later or use rate limiter

919

scheduleRetry(params, waitSeconds);

920

return null;

921

}

922

```

923

924

### 4. Implement Graceful Degradation

925

926

```java

927

import com.anthropic.errors.*;

928

929

public Message createMessageWithFallback(MessageCreateParams params) {

930

try {

931

return client.messages().create(params);

932

933

} catch (RateLimitException e) {

934

// Queue for later processing

935

queueForLater(params);

936

return createPlaceholderMessage("Queued due to rate limit");

937

938

} catch (InternalServerException e) {

939

// Use cached response if available

940

return getCachedResponse(params)

941

.orElseGet(() -> createPlaceholderMessage("Server error"));

942

943

} catch (AnthropicException e) {

944

// Log and return error response

945

logError(e);

946

return createErrorMessage(e.getMessage());

947

}

948

}

949

```

950

951

### 5. Don't Retry Non-Retryable Errors

952

953

```java

954

import com.anthropic.errors.*;

955

956

public boolean isRetryable(AnthropicException e) {

957

return e instanceof RateLimitException

958

|| e instanceof InternalServerException

959

|| e instanceof AnthropicIoException

960

|| e instanceof AnthropicRetryableException;

961

}

962

963

public Message createWithConditionalRetry(MessageCreateParams params) {

964

try {

965

return client.messages().create(params);

966

} catch (AnthropicException e) {

967

if (isRetryable(e)) {

968

// Retry logic

969

return retryCreate(params);

970

} else {

971

// Don't retry, handle error

972

System.err.println("Non-retryable error: " + e.getMessage());

973

throw e;

974

}

975

}

976

}

977

```

978

979

### 6. Validate Responses When Needed

980

981

Enable response validation to catch data issues early:

982

983

```java

984

import com.anthropic.core.RequestOptions;

985

import com.anthropic.models.messages.Message;

986

987

// Per-request validation

988

Message message = client.messages().create(

989

params,

990

RequestOptions.builder()

991

.responseValidation(true)

992

.build()

993

);

994

995

// Or configure globally

996

AnthropicClient client = AnthropicOkHttpClient.builder()

997

.fromEnv()

998

.responseValidation(true)

999

.build();

1000

```

1001

1002

### 7. Handle Streaming Interruptions

1003

1004

```java

1005

import com.anthropic.core.http.StreamResponse;

1006

import com.anthropic.errors.SseException;

1007

import com.anthropic.helpers.MessageAccumulator;

1008

1009

public Message createWithStreamRecovery(MessageCreateParams params) {

1010

MessageAccumulator accumulator = MessageAccumulator.create();

1011

1012

try (StreamResponse<RawMessageStreamEvent> stream =

1013

client.messages().createStreaming(params)) {

1014

1015

stream.stream()

1016

.peek(accumulator::accumulate)

1017

.forEach(this::processEvent);

1018

1019

return accumulator.message();

1020

1021

} catch (SseException e) {

1022

System.err.println("Stream interrupted: " + e.getMessage());

1023

1024

// Return partial message if useful

1025

if (accumulator.message() != null) {

1026

System.out.println("Returning partial message");

1027

return accumulator.message();

1028

}

1029

throw e;

1030

}

1031

}

1032

```

1033

1034

### 8. Clean Up Resources

1035

1036

Always close clients and streams properly:

1037

1038

```java

1039

import com.anthropic.client.AnthropicClient;

1040

import com.anthropic.core.http.StreamResponse;

1041

1042

// Close client when done

1043

try (AnthropicClient client = AnthropicOkHttpClient.fromEnv()) {

1044

// Use client

1045

} // Automatically closed

1046

1047

// Close streams

1048

try (StreamResponse<RawMessageStreamEvent> stream =

1049

client.messages().createStreaming(params)) {

1050

stream.stream().forEach(this::processEvent);

1051

} // Automatically closed

1052

```

1053

1054

### 9. Provide Context in Errors

1055

1056

Add application context to error messages:

1057

1058

```java

1059

import com.anthropic.errors.AnthropicException;

1060

1061

public void processUserRequest(String userId, String requestText) {

1062

try {

1063

MessageCreateParams params = MessageCreateParams.builder()

1064

.addUserMessage(requestText)

1065

.model(Model.CLAUDE_SONNET_4_20250514)

1066

.maxTokens(1024L)

1067

.build();

1068

1069

Message message = client.messages().create(params);

1070

saveResponse(userId, message);

1071

1072

} catch (AnthropicException e) {

1073

// Include application context

1074

System.err.println("Failed to process request for user: " + userId);

1075

System.err.println("Request text length: " + requestText.length());

1076

System.err.println("Error: " + e.getMessage());

1077

1078

if (e instanceof AnthropicServiceException) {

1079

AnthropicServiceException se = (AnthropicServiceException) e;

1080

System.err.println("Status: " + se.statusCode());

1081

1082

String requestId = se.headers().get("request-id")

1083

.stream().findFirst().orElse("unknown");

1084

System.err.println("Request ID: " + requestId);

1085

}

1086

1087

throw new RuntimeException("Failed to process request for user " + userId, e);

1088

}

1089

}

1090

```

1091

1092

## Summary

1093

1094

The Anthropic Java SDK provides comprehensive error handling through a well-structured exception hierarchy:

1095

1096

- **Service exceptions** map to specific HTTP status codes for precise error handling

1097

- **Streaming exceptions** handle SSE-specific errors during streaming operations

1098

- **I/O exceptions** capture network and connectivity issues

1099

- **Retryable exceptions** indicate transient failures that can be retried

1100

- **Invalid data exceptions** catch parsing and validation errors

1101

1102

All exceptions provide rich context including status codes, headers, and request IDs for debugging. The SDK automatically retries appropriate errors with exponential backoff, while allowing customization of retry behavior.

1103

1104

Following best practices like logging request IDs, implementing monitoring, handling rate limits gracefully, and using appropriate retry strategies will help build robust applications with the Anthropic SDK.

1105