spring-projects/spring-ai · error · RuntimeException

Failed to extract record field types

Error message

Failed to extract record field types

What it means

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.

Source

Thrown at spring-ai-model/src/main/java/org/springframework/ai/tool/augment/ToolInputSchemaAugmenter.java:73

				// Get the annotation from the corresponding field, not the record
				// component
				ToolParam toolParam = null;
				try {
					var field = recordClass.getDeclaredField(c.getName());
					toolParam = field.getAnnotation(ToolParam.class);
				}
				catch (NoSuchFieldException e) {
					// Field not found, toolParam remains null
				}

				return new AugmentedArgumentType(c.getName(), c.getGenericType(),
						toolParam != null ? toolParam.description() : "no description",
						toolParam != null ? toolParam.required() : false);
			}).toList();

		}
		catch (Exception e) {
			throw new RuntimeException("Failed to extract record field types", e);
		}
	}

	public static String augmentToolInputSchema(String jsonSchemaString, String propertyName, Type propertyType,
			String description, boolean required) {

		return augmentToolInputSchema(jsonSchemaString,
				List.of(new AugmentedArgumentType(propertyName, propertyType, description, required)));
	}

	public static String augmentToolInputSchema(String jsonSchemaString, List<AugmentedArgumentType> argumentType) {

		try {

			ObjectNode schemaObjectNode = (ObjectNode) JacksonUtils.getDefaultJsonMapper().readTree(jsonSchemaString);

			// Handle properties
			ObjectNode propertiesNode;

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Ensure argumentType is a Java record: convert the POJO to a record with matching components.
  2. Inspect the wrapped cause (e.getCause()) for the exact reflective failure.
  3. Annotate record components with @ToolParam(description=..., required=...) so extraction completes cleanly.

Example fix

// before
builder().argumentType(UserArgsPojo.class) // not a record
// after
public record UserArgs(@ToolParam(description = "User id", required = true) String userId) {}
builder().argumentType(UserArgs.class)
Defensive patterns

Strategy: validation

Validate before calling

if (argumentType == null || !argumentType.isRecord()) {
    throw new IllegalArgumentException("argumentType must be a Java record, got: " + argumentType);
}

Type guard

static boolean isRecordType(Class<?> c) { return c != null && c.isRecord(); }

Try / catch

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; }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/f97aa86ed460b985. Report an issue: GitHub.