0
# Structured Outputs
1
2
Anthropic Structured Outputs (beta) is a feature that ensures the model will always generate responses that adhere to a supplied JSON schema. The SDK provides automatic JSON schema derivation from Java classes, enabling type-safe structured responses without manual schema definition.
3
4
## Overview
5
6
Structured Outputs guarantees that Claude's responses conform to your specified JSON schema, making it ideal for extracting structured data, generating consistent formats, and building reliable integrations. The SDK automatically converts Java classes into JSON schemas and deserializes responses back into Java objects.
7
8
Key features:
9
- Automatic JSON schema generation from Java class structure
10
- Type-safe response handling with compile-time checking
11
- Support for nested classes, collections, and optional fields
12
- Jackson and Swagger annotations for schema customization
13
- Streaming support with JSON accumulation
14
- Local schema validation before API calls
15
16
## Output Format Configuration
17
18
Configure structured outputs by calling `outputFormat(Class<T>)` on the message builder. This method derives a JSON schema from the provided class and returns a generic builder for type-safe parameter construction.
19
20
```java { .api }
21
public <T> StructuredMessageCreateParams.Builder<T> outputFormat(Class<T> outputClass);
22
public <T> StructuredMessageCreateParams.Builder<T> outputFormat(
23
Class<T> outputClass,
24
JsonSchemaLocalValidation validation
25
);
26
```
27
28
**Parameters:**
29
- `outputClass` - The Java class to derive the JSON schema from
30
- `validation` - Whether to validate the schema locally (default: `JsonSchemaLocalValidation.YES`)
31
32
**Returns:** A `StructuredMessageCreateParams.Builder<T>` for building type-safe parameters
33
34
## StructuredMessageCreateParams
35
36
Generic builder type for creating message requests with structured outputs. When you call `outputFormat(Class<T>)` on a standard builder, it returns this specialized builder parameterized with your output type.
37
38
```java { .api }
39
public final class StructuredMessageCreateParams<T> {
40
public static <T> Builder<T> builder();
41
42
public static final class Builder<T> {
43
public Builder<T> model(Model model);
44
public Builder<T> maxTokens(long maxTokens);
45
public Builder<T> messages(List<MessageParam> messages);
46
public Builder<T> addMessage(MessageParam message);
47
public Builder<T> addUserMessage(String content);
48
public Builder<T> addAssistantMessage(String content);
49
public Builder<T> temperature(Double temperature);
50
public Builder<T> system(String system);
51
public Builder<T> tools(List<ToolUnion> tools);
52
public Builder<T> addTool(Tool tool);
53
public Builder<T> toolChoice(ToolChoice toolChoice);
54
public Builder<T> metadata(Metadata metadata);
55
public Builder<T> stopSequences(List<String> stopSequences);
56
public Builder<T> topK(Long topK);
57
public Builder<T> topP(Double topP);
58
public StructuredMessageCreateParams<T> build();
59
}
60
}
61
```
62
63
## Defining Java Classes
64
65
Classes used for structured outputs must follow specific rules to ensure valid JSON schema generation:
66
67
### Field Declaration Rules
68
69
**Public fields** are included in the JSON schema by default:
70
```java
71
class Person {
72
public String name; // Included
73
public int birthYear; // Included
74
private String internal; // Excluded by default
75
}
76
```
77
78
**Private fields with public getters** are included using the getter method name:
79
```java
80
class Person {
81
private String name;
82
83
public String getName() { // Creates "name" property
84
return name;
85
}
86
}
87
```
88
89
**Non-conventional getter names** require `@JsonProperty` annotation:
90
```java
91
class Person {
92
private String name;
93
94
@JsonProperty
95
public String fullName() { // Creates "fullName" property
96
return name;
97
}
98
}
99
```
100
101
### Nested Classes and Collections
102
103
Classes can contain fields of other class types and use standard Java collections:
104
105
```java
106
class Person {
107
public String name;
108
public int birthYear;
109
}
110
111
class Book {
112
public String title;
113
public Person author; // Nested class
114
public int publicationYear;
115
}
116
117
class BookList {
118
public List<Book> books; // Collection of nested classes
119
}
120
```
121
122
Supported collection types:
123
- `List<T>`
124
- `Set<T>`
125
- `Collection<T>`
126
- Arrays (`T[]`)
127
128
### Required Properties
129
130
Each class must define at least one property. A validation error occurs if:
131
- The class has no fields or getter methods
132
- All public fields/getters are annotated with `@JsonIgnore`
133
- All non-public fields/getters lack `@JsonProperty` annotations
134
- A field uses `Map` type (maps have no named properties)
135
136
## Optional Fields
137
138
Use `java.util.Optional<T>` to represent optional properties. The AI model decides whether to provide a value or leave it empty.
139
140
```java
141
import java.util.Optional;
142
143
class Book {
144
public String title; // Required
145
public Person author; // Required
146
public int publicationYear; // Required
147
public Optional<String> isbn; // Optional
148
public Optional<String> subtitle; // Optional
149
}
150
```
151
152
**Usage:**
153
```java
154
StructuredMessage<Book> response = client.beta().messages().create(params);
155
Book book = response.content().get(0).text().text();
156
157
// Check optional fields
158
if (book.isbn.isPresent()) {
159
System.out.println("ISBN: " + book.isbn.get());
160
} else {
161
System.out.println("No ISBN provided");
162
}
163
```
164
165
## JSON Schema Derivation
166
167
The SDK automatically generates JSON schemas from Java class structure:
168
169
**Input class:**
170
```java
171
class Book {
172
public String title;
173
public Person author;
174
public int publicationYear;
175
public Optional<String> isbn;
176
}
177
178
class Person {
179
public String name;
180
public int birthYear;
181
}
182
```
183
184
**Generated schema (conceptual):**
185
```json
186
{
187
"type": "object",
188
"properties": {
189
"title": { "type": "string" },
190
"author": {
191
"type": "object",
192
"properties": {
193
"name": { "type": "string" },
194
"birthYear": { "type": "integer" }
195
},
196
"required": ["name", "birthYear"]
197
},
198
"publicationYear": { "type": "integer" },
199
"isbn": { "type": "string" }
200
},
201
"required": ["title", "author", "publicationYear"]
202
}
203
```
204
205
Schema generation rules:
206
- Public fields and getter methods become schema properties
207
- Field names become property names (using getter naming conventions)
208
- Java types map to JSON schema types (String → string, int/long → integer, etc.)
209
- `Optional<T>` fields are not included in required array
210
- Nested classes become nested object schemas
211
- Collections become array schemas with item types
212
213
## BetaJsonOutputFormat
214
215
Low-level class for manually defining JSON schemas when automatic derivation is not suitable. This provides complete control over schema structure.
216
217
```java { .api }
218
public final class BetaJsonOutputFormat {
219
public static Builder builder();
220
221
public static final class Builder {
222
public Builder type(Type type);
223
public Builder schema(JsonSchema schema);
224
public Builder build();
225
}
226
227
public enum Type {
228
JSON_SCHEMA
229
}
230
231
public static final class JsonSchema {
232
public static Builder builder();
233
234
public static final class Builder {
235
public Builder type(String type);
236
public Builder properties(Map<String, Object> properties);
237
public Builder required(List<String> required);
238
public Builder additionalProperties(boolean allowed);
239
public JsonSchema build();
240
}
241
}
242
}
243
```
244
245
**Manual schema example:**
246
```java
247
BetaJsonOutputFormat format = BetaJsonOutputFormat.builder()
248
.type(BetaJsonOutputFormat.Type.JSON_SCHEMA)
249
.schema(BetaJsonOutputFormat.JsonSchema.builder()
250
.type("object")
251
.properties(Map.of(
252
"title", Map.of("type", "string"),
253
"year", Map.of("type", "integer")
254
))
255
.required(List.of("title", "year"))
256
.build())
257
.build();
258
```
259
260
## StructuredMessage
261
262
Response type for structured output requests. Extends the standard `Message` class with typed content access methods.
263
264
```java { .api }
265
public final class StructuredMessage<T> extends Message {
266
@Override
267
public List<ContentBlock> content();
268
269
// Type-safe content access
270
public T getStructuredContent();
271
public Optional<T> getStructuredContentSafe();
272
}
273
```
274
275
**Usage:**
276
```java
277
StructuredMessageCreateParams<BookList> params = MessageCreateParams.builder()
278
.model(Model.CLAUDE_SONNET_4_5_20250929)
279
.maxTokens(2048)
280
.outputFormat(BookList.class)
281
.addUserMessage("List famous novels.")
282
.build();
283
284
StructuredMessage<BookList> response = client.beta().messages().create(params);
285
286
// Access structured content
287
response.content().stream()
288
.flatMap(block -> block.text().stream())
289
.flatMap(textBlock -> textBlock.text().books.stream())
290
.forEach(book -> System.out.println(book.title + " by " + book.author.name));
291
```
292
293
## Local Schema Validation
294
295
The SDK performs local validation before sending requests to ensure schemas comply with Anthropic's restrictions. This catches errors early and provides detailed feedback.
296
297
```java { .api }
298
public enum JsonSchemaLocalValidation {
299
YES,
300
NO
301
}
302
```
303
304
### Validation Behavior
305
306
**Local Validation (default):**
307
- Validates schema against Anthropic's subset of JSON Schema specification
308
- Checks for unsupported data types and constructs
309
- Throws exception with detailed error message if validation fails
310
- No API request sent if validation fails
311
312
**Remote Validation:**
313
- Performed by Anthropic API after receiving request
314
- May differ from local validation if SDK version is outdated
315
316
### When to Disable Local Validation
317
318
Disable local validation when:
319
- Using newer Anthropic API features not yet supported by SDK version
320
- Local validation incorrectly rejects valid schemas (version mismatch)
321
- Debugging schema compatibility issues
322
323
**Disable validation:**
324
```java
325
StructuredMessageCreateParams<BookList> params = MessageCreateParams.builder()
326
.model(Model.CLAUDE_SONNET_4_5_20250929)
327
.maxTokens(2048)
328
.outputFormat(BookList.class, JsonSchemaLocalValidation.NO)
329
.addUserMessage("List famous novels.")
330
.build();
331
```
332
333
## Jackson Annotations
334
335
Jackson Databind annotations control schema generation and add descriptive metadata.
336
337
### @JsonClassDescription
338
339
Adds a description to a class in the generated schema.
340
341
```java { .api }
342
@Target(ElementType.TYPE)
343
@Retention(RetentionPolicy.RUNTIME)
344
public @interface JsonClassDescription {
345
String value();
346
}
347
```
348
349
**Example:**
350
```java
351
import com.fasterxml.jackson.annotation.JsonClassDescription;
352
353
@JsonClassDescription("The details of one published book")
354
class Book {
355
public String title;
356
public Person author;
357
public int publicationYear;
358
}
359
```
360
361
### @JsonPropertyDescription
362
363
Adds a description to a field or getter method in the generated schema.
364
365
```java { .api }
366
@Target({ElementType.FIELD, ElementType.METHOD})
367
@Retention(RetentionPolicy.RUNTIME)
368
public @interface JsonPropertyDescription {
369
String value();
370
}
371
```
372
373
**Example:**
374
```java
375
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
376
377
class Person {
378
@JsonPropertyDescription("The first name and surname of the person")
379
public String name;
380
381
public int birthYear;
382
383
@JsonPropertyDescription("The year the person died, or 'present' if the person is living")
384
public String deathYear;
385
}
386
```
387
388
### @JsonIgnore
389
390
Excludes a public field or getter method from the generated schema.
391
392
```java { .api }
393
@Target({ElementType.FIELD, ElementType.METHOD})
394
@Retention(RetentionPolicy.RUNTIME)
395
public @interface JsonIgnore {
396
boolean value() default true;
397
}
398
```
399
400
**Example:**
401
```java
402
import com.fasterxml.jackson.annotation.JsonIgnore;
403
404
class Book {
405
public String title;
406
public Person author;
407
public int publicationYear;
408
409
@JsonIgnore
410
public String internalNotes; // Excluded from schema
411
}
412
```
413
414
### @JsonProperty
415
416
Includes a non-public field or getter method in the generated schema, or customizes property names.
417
418
```java { .api }
419
@Target({ElementType.FIELD, ElementType.METHOD})
420
@Retention(RetentionPolicy.RUNTIME)
421
public @interface JsonProperty {
422
String value() default "";
423
boolean required() default true;
424
}
425
```
426
427
**Example:**
428
```java
429
import com.fasterxml.jackson.annotation.JsonProperty;
430
431
class Book {
432
@JsonProperty
433
private String title; // Included despite being private
434
435
private String author;
436
437
@JsonProperty("author_name")
438
public String getAuthor() { // Property named "author_name"
439
return author;
440
}
441
}
442
```
443
444
**Note:** The `required` attribute is ignored. Anthropic schemas require all properties to be marked as required (unless wrapped in `Optional<T>`).
445
446
## Swagger Annotations
447
448
Swagger/OpenAPI annotations add type-specific constraints to schema properties.
449
450
### @Schema
451
452
Adds constraints and metadata to fields and classes.
453
454
```java { .api }
455
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
456
@Retention(RetentionPolicy.RUNTIME)
457
public @interface Schema {
458
String description() default "";
459
String format() default "";
460
String minimum() default "";
461
String maximum() default "";
462
String pattern() default "";
463
int minLength() default Integer.MIN_VALUE;
464
int maxLength() default Integer.MAX_VALUE;
465
}
466
```
467
468
**Supported constraints:**
469
- `description` - Property or class description
470
- `format` - String format (e.g., "date", "date-time", "email", "uri")
471
- `minimum` - Minimum numeric value (as string)
472
- `maximum` - Maximum numeric value (as string)
473
- `pattern` - Regular expression pattern for strings
474
- `minLength` - Minimum string length
475
- `maxLength` - Maximum string length
476
477
**Example:**
478
```java
479
import io.swagger.v3.oas.annotations.media.Schema;
480
481
class Article {
482
public String title;
483
484
@Schema(format = "date")
485
public String publicationDate;
486
487
@Schema(minimum = "1", maximum = "10000")
488
public int pageCount;
489
490
@Schema(pattern = "^[A-Z]{3}-\\d{4}$")
491
public String articleCode;
492
493
@Schema(minLength = 10, maxLength = 500)
494
public String summary;
495
}
496
```
497
498
### @ArraySchema
499
500
Adds array-specific constraints to collection fields.
501
502
```java { .api }
503
@Target({ElementType.FIELD, ElementType.METHOD})
504
@Retention(RetentionPolicy.RUNTIME)
505
public @interface ArraySchema {
506
int minItems() default Integer.MIN_VALUE;
507
int maxItems() default Integer.MAX_VALUE;
508
boolean uniqueItems() default false;
509
}
510
```
511
512
**Example:**
513
```java
514
import io.swagger.v3.oas.annotations.media.ArraySchema;
515
516
class Article {
517
@ArraySchema(minItems = 1, maxItems = 10)
518
public List<String> authors;
519
520
@ArraySchema(uniqueItems = true)
521
public List<String> tags;
522
523
public String title;
524
}
525
```
526
527
**Annotation precedence:** When both Jackson and Swagger annotations set the same schema field, Jackson annotations take precedence. For example:
528
529
```java
530
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
531
import io.swagger.v3.oas.annotations.media.Schema;
532
533
class MyObject {
534
@Schema(description = "Swagger description")
535
@JsonPropertyDescription("Jackson description")
536
public String myProperty; // Description will be "Jackson description"
537
}
538
```
539
540
## Streaming Integration
541
542
Structured outputs work with streaming by accumulating JSON strings from stream events and deserializing them into Java objects after the stream completes.
543
544
### BetaMessageAccumulator
545
546
Helper class for accumulating streaming events into a complete message.
547
548
```java { .api }
549
public final class BetaMessageAccumulator {
550
public BetaMessageAccumulator();
551
552
public void accumulate(RawMessageStreamEvent event);
553
public BetaMessage message();
554
public <T> StructuredMessage<T> message(Class<T> outputClass);
555
}
556
```
557
558
**Streaming example:**
559
```java
560
import com.anthropic.helpers.BetaMessageAccumulator;
561
import com.anthropic.models.beta.messages.StructuredMessageCreateParams;
562
import com.anthropic.core.http.StreamResponse;
563
564
// Create structured output parameters
565
StructuredMessageCreateParams<BookList> params = MessageCreateParams.builder()
566
.model(Model.CLAUDE_SONNET_4_5_20250929)
567
.maxTokens(2048)
568
.outputFormat(BookList.class)
569
.addUserMessage("List famous novels from the 20th century.")
570
.build();
571
572
// Create streaming request
573
StreamResponse<RawMessageStreamEvent> stream =
574
client.beta().messages().createStreaming(params);
575
576
// Accumulate events
577
BetaMessageAccumulator accumulator = new BetaMessageAccumulator();
578
stream.stream().forEach(event -> {
579
accumulator.accumulate(event);
580
581
// Process events as they arrive
582
if (event.isContentBlockDelta()) {
583
System.out.print(event.asContentBlockDelta().delta().text());
584
}
585
});
586
587
// Get structured message after streaming completes
588
StructuredMessage<BookList> message = accumulator.message(BookList.class);
589
BookList books = message.getStructuredContent();
590
591
System.out.println("\n\nComplete book list:");
592
books.books.forEach(book ->
593
System.out.println(book.title + " by " + book.author.name)
594
);
595
```
596
597
## Generic Type Erasure
598
599
Java's generic type erasure prevents runtime type information from being available for local variables and parameters. This limits schema derivation to class fields only.
600
601
**Works (field with generic type):**
602
```java
603
class BookList {
604
public List<Book> books; // Generic type preserved in field metadata
605
}
606
607
StructuredMessageCreateParams<BookList> params = MessageCreateParams.builder()
608
.outputFormat(BookList.class) // Can derive schema from BookList.books field
609
.build();
610
```
611
612
**Does NOT work (local variable with generic type):**
613
```java
614
List<Book> books = new ArrayList<>();
615
616
StructuredMessageCreateParams<List<Book>> params = MessageCreateParams.builder()
617
.outputFormat(books.getClass()) // Type erasure: only knows it's a List, not List<Book>
618
.build(); // Cannot generate valid schema
619
```
620
621
**Solution:** Always wrap collections in a class with typed fields:
622
```java
623
class BookCollection {
624
public List<Book> items;
625
}
626
627
StructuredMessageCreateParams<BookCollection> params = MessageCreateParams.builder()
628
.outputFormat(BookCollection.class)
629
.build();
630
```
631
632
## Error Handling
633
634
### JSON Conversion Errors
635
636
When JSON response cannot be converted to the specified Java class, an exception is thrown with the JSON response included for diagnosis.
637
638
**Common causes:**
639
- Response truncated (reached max_tokens limit)
640
- JSON syntax errors in response
641
- Type mismatches between schema and response
642
- Missing required fields in response
643
644
**Example error:**
645
```
646
Failed to deserialize JSON response into class BookList
647
JSON response: {"books":[{"title":"1984","author":{"name":"George Orwell"
648
```
649
650
**Security consideration:** Error messages include the JSON response, which may contain sensitive information. Avoid logging errors directly in production environments, or redact sensitive data first.
651
652
### Schema Validation Errors
653
654
Local validation errors occur when the derived schema violates Anthropic's restrictions.
655
656
**Common causes:**
657
- Unsupported data types (e.g., `LocalDate` without custom serializer)
658
- Classes with no properties (all fields excluded or no fields defined)
659
- Map types used as field types
660
- Unsupported constraint values
661
662
**Example error:**
663
```
664
Schema validation failed for class Book:
665
- Property 'publishDate' has unsupported type 'java.time.LocalDate'
666
- Use String with @Schema(format="date") instead
667
```
668
669
**Resolution:** Either fix the class definition or disable local validation with `JsonSchemaLocalValidation.NO` if using newer API features.
670
671
## Best Practices
672
673
### Field Definition Patterns
674
675
**Prefer public fields for simple data classes:**
676
```java
677
class Book {
678
public String title;
679
public String author;
680
public int year;
681
}
682
```
683
684
**Use private fields with getters for encapsulation:**
685
```java
686
class Book {
687
private String title;
688
private String author;
689
690
public String getTitle() { return title; }
691
public String getAuthor() { return author; }
692
}
693
```
694
695
**Use `Optional<T>` sparingly:**
696
```java
697
class Book {
698
public String title; // Core data: required
699
public String author; // Core data: required
700
public Optional<String> isbn; // Truly optional metadata
701
}
702
```
703
704
### Schema Validation
705
706
**Let local validation catch errors early:**
707
```java
708
try {
709
StructuredMessageCreateParams<Book> params = MessageCreateParams.builder()
710
.outputFormat(Book.class) // Validates schema immediately
711
.build();
712
} catch (AnthropicException e) {
713
System.err.println("Invalid schema: " + e.getMessage());
714
// Fix class definition before making API calls
715
}
716
```
717
718
**Disable validation only when necessary:**
719
```java
720
// Only use when local validation is incorrect
721
params = MessageCreateParams.builder()
722
.outputFormat(Book.class, JsonSchemaLocalValidation.NO)
723
.build();
724
```
725
726
### Annotations
727
728
**Add descriptions for better AI responses:**
729
```java
730
@JsonClassDescription("A published book with author and publication information")
731
class Book {
732
@JsonPropertyDescription("The full title of the book")
733
public String title;
734
735
@JsonPropertyDescription("The primary author of the book")
736
public Person author;
737
738
@JsonPropertyDescription("The year the book was first published")
739
public int publicationYear;
740
}
741
```
742
743
**Add constraints for validation:**
744
```java
745
class Article {
746
@Schema(minLength = 1, maxLength = 200)
747
public String title;
748
749
@ArraySchema(minItems = 1, maxItems = 10)
750
public List<String> authors;
751
752
@Schema(minimum = "1", maximum = "1000")
753
public int pageCount;
754
}
755
```
756
757
### Complete Example
758
759
**Define data classes:**
760
```java
761
import com.fasterxml.jackson.annotation.JsonClassDescription;
762
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
763
import io.swagger.v3.oas.annotations.media.ArraySchema;
764
import io.swagger.v3.oas.annotations.media.Schema;
765
import java.util.List;
766
import java.util.Optional;
767
768
@JsonClassDescription("A person with biographical information")
769
class Person {
770
@JsonPropertyDescription("The full name of the person")
771
public String name;
772
773
@Schema(minimum = "1800", maximum = "2100")
774
public int birthYear;
775
776
@JsonPropertyDescription("The year the person died, or empty if still living")
777
public Optional<Integer> deathYear;
778
}
779
780
@JsonClassDescription("A published book with metadata")
781
class Book {
782
@JsonPropertyDescription("The full title of the book")
783
@Schema(minLength = 1, maxLength = 200)
784
public String title;
785
786
@JsonPropertyDescription("The primary author of the book")
787
public Person author;
788
789
@Schema(minimum = "1800", maximum = "2100")
790
public int publicationYear;
791
792
@Schema(pattern = "^(?:ISBN(?:-13)?:?\\ )?(?=[0-9]{13}$|(?=(?:[0-9]+[-\\ ]){4})[-\\ 0-9]{17}$)97[89][-\\ ]?[0-9]{1,5}[-\\ ]?[0-9]+[-\\ ]?[0-9]+[-\\ ]?[0-9]$")
793
public Optional<String> isbn;
794
}
795
796
@JsonClassDescription("A collection of books")
797
class BookList {
798
@ArraySchema(minItems = 1, maxItems = 20)
799
public List<Book> books;
800
}
801
```
802
803
**Create and execute request:**
804
```java
805
import com.anthropic.client.AnthropicClient;
806
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
807
import com.anthropic.models.beta.messages.MessageCreateParams;
808
import com.anthropic.models.beta.messages.StructuredMessage;
809
import com.anthropic.models.beta.messages.StructuredMessageCreateParams;
810
import com.anthropic.models.messages.Model;
811
812
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
813
814
StructuredMessageCreateParams<BookList> params = MessageCreateParams.builder()
815
.model(Model.CLAUDE_SONNET_4_5_20250929)
816
.maxTokens(4096)
817
.outputFormat(BookList.class)
818
.addUserMessage("List 5 famous novels from the late 20th century (1950-2000).")
819
.build();
820
821
try {
822
StructuredMessage<BookList> response = client.beta().messages().create(params);
823
BookList result = response.getStructuredContent();
824
825
System.out.println("Famous 20th century novels:");
826
result.books.forEach(book -> {
827
System.out.printf("%s by %s (%d)%n",
828
book.title,
829
book.author.name,
830
book.publicationYear);
831
book.isbn.ifPresent(isbn -> System.out.println(" ISBN: " + isbn));
832
});
833
} catch (AnthropicException e) {
834
System.err.println("Error: " + e.getMessage());
835
}
836
```
837