{"record":{"id":"f97aa86ed460b985","repo":"spring-projects/spring-ai","slug":"failed-to-extract-record-field-types","errorCode":null,"errorMessage":"Failed to extract record field types","messagePattern":"Failed to extract record field types","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"spring-ai-model/src/main/java/org/springframework/ai/tool/augment/ToolInputSchemaAugmenter.java","lineNumber":73,"sourceCode":"\t\t\t\t// Get the annotation from the corresponding field, not the record\n\t\t\t\t// component\n\t\t\t\tToolParam toolParam = null;\n\t\t\t\ttry {\n\t\t\t\t\tvar field = recordClass.getDeclaredField(c.getName());\n\t\t\t\t\ttoolParam = field.getAnnotation(ToolParam.class);\n\t\t\t\t}\n\t\t\t\tcatch (NoSuchFieldException e) {\n\t\t\t\t\t// Field not found, toolParam remains null\n\t\t\t\t}\n\n\t\t\t\treturn new AugmentedArgumentType(c.getName(), c.getGenericType(),\n\t\t\t\t\t\ttoolParam != null ? toolParam.description() : \"no description\",\n\t\t\t\t\t\ttoolParam != null ? toolParam.required() : false);\n\t\t\t}).toList();\n\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to extract record field types\", e);\n\t\t}\n\t}\n\n\tpublic static String augmentToolInputSchema(String jsonSchemaString, String propertyName, Type propertyType,\n\t\t\tString description, boolean required) {\n\n\t\treturn augmentToolInputSchema(jsonSchemaString,\n\t\t\t\tList.of(new AugmentedArgumentType(propertyName, propertyType, description, required)));\n\t}\n\n\tpublic static String augmentToolInputSchema(String jsonSchemaString, List<AugmentedArgumentType> argumentType) {\n\n\t\ttry {\n\n\t\t\tObjectNode schemaObjectNode = (ObjectNode) JacksonUtils.getDefaultJsonMapper().readTree(jsonSchemaString);\n\n\t\t\t// Handle properties\n\t\t\tObjectNode propertiesNode;","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/spring-ai-model/src/main/java/org/springframework/ai/tool/augment/ToolInputSchemaAugmenter.java#L55-L91","documentation":"ToolInputSchemaAugmenter.toAugmentedArgumentTypes() reflects over a record's components to derive augmented argument types (name, type, description, required). Any exception during that reflective extraction (e.g. the argumentType is not a record, access errors) is wrapped in a RuntimeException with this message and the original cause attached.","triggerScenarios":"Passing a non-record class (plain POJO, interface, String, etc.) as argumentType to AugmentedToolCallbackProvider or toAugmentedArgumentTypes; a record type whose component accessors cannot be reflectively read.","commonSituations":"Upgrading from POJO-based tool arguments to the record requirement in newer Spring AI; accidentally passing the tool's return type or a generic Map instead of the request record.","solutions":["Ensure argumentType is a Java record: convert the POJO to a record with matching components.","Inspect the wrapped cause (e.getCause()) for the exact reflective failure.","Annotate record components with @ToolParam(description=..., required=...) so extraction completes cleanly."],"exampleFix":"// before\nbuilder().argumentType(UserArgsPojo.class) // not a record\n// after\npublic record UserArgs(@ToolParam(description = \"User id\", required = true) String userId) {}\nbuilder().argumentType(UserArgs.class)","handlingStrategy":"validation","validationCode":"if (argumentType == null || !argumentType.isRecord()) {\n    throw new IllegalArgumentException(\"argumentType must be a Java record, got: \" + argumentType);\n}","typeGuard":"static boolean isRecordType(Class<?> c) { return c != null && c.isRecord(); }","tryCatchPattern":"try { provider = builder.build(); } catch (RuntimeException e) { if (e.getMessage().contains(\"Failed to extract record field types\")) throw new IllegalArgumentException(\"argumentType must be a record\", e); throw e; }","preventionTips":["Define tool argument types exclusively as Java records with @ToolParam annotations.","Add a unit test that builds the provider for every registered tool argument type."],"tags":["reflection","records","schema","spring-ai"],"backgroundTag":"incompatible-source-type","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}