spring-projects/spring-ai · error · IllegalArgumentException
Type must be a Function, BiFunction, Function1 or…
Error message
Type must be a Function, BiFunction, Function1 or Function2. Found:
What it means
Thrown when Spring AI tries to infer the input/output types of a functional bean but the bean's type is not a supported functional shape (Function, BiFunction, Kotlin Function1/Function2, or Supplier variants). The resolved generic type came back as ResolvableType.NONE.
Solutions
- Change the bean to implement java.util.function.Function<I,O> (or BiFunction / Kotlin Function1/Function2)
- Wrap unsupported types with an adapter lambda typed as Function
- Give the lambda an explicit target type (Function<Input, Output> f = in -> ...) rather than var/Object
- Specify the tool's input/output types explicitly instead of relying on type inference
Example fix
// before
@Bean
public TriFunction<String, String, String, String> myTool() { ... }
// after
@Bean
public Function<MyRequest, String> myTool() {
return req -> doWork(req.a(), req.b());
} Defensive patterns
Strategy: type-guard
Validate before calling
if (!(bean instanceof Function) && !(bean instanceof BiFunction) && !(bean instanceof Supplier)) {
throw new IllegalStateException(beanName + " must be a Function/BiFunction/Supplier to be exposed as a tool");
} Type guard
boolean isFunctionalTool(Object bean) {
return bean instanceof Function<?, ?> || bean instanceof BiFunction<?, ?, ?> || bean instanceof Supplier<?>;
} Prevention
- Always type functional beans as java.util.function types, never Object or custom interfaces
- Give lambdas explicit target types instead of var
- Limit Kotlin lambdas to 1-2 parameters when exposing as tools
When it happens
Trigger: Registering a bean whose type implements none of Function/BiFunction/Consumer-type contracts (e.g. a custom interface, a 3-arg function, or a lambda typed only as Object) and then exposing it as a tool via FunctionToolCallback or ToolCallbacks.from.
Common situations: Using a custom functional interface instead of java.util.function.Function; Kotlin lambdas typed as Function3+; raw lambdas assigned to Object variables; generics erased so ResolvableType sees NONE.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Anthropic cache breakpoint limit (4) reached. Additional…
- Anthropic Citations API requires all documents to have…
- argumentConsumer is required
- argumentType is required
- Async complete methods should use McpAsyncRequestContext…
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/08eac5519d522975.
Report an issue: GitHub.
Appendix: source
Thrown at spring-ai-model/src/main/java/org/springframework/ai/tool/resolution/TypeResolverHelper.java:267
functionArgumentResolvableType = functionType.as(Supplier.class);
}
else if (Consumer.class.isAssignableFrom(resolvableClass)) {
functionArgumentResolvableType = functionType.as(Consumer.class);
}
else if (KotlinDetector.isKotlinPresent()) {
if (KotlinDelegate.isKotlinFunction(resolvableClass)) {
functionArgumentResolvableType = KotlinDelegate.adaptToKotlinFunctionType(functionType);
}
else if (KotlinDelegate.isKotlinBiFunction(resolvableClass)) {
functionArgumentResolvableType = KotlinDelegate.adaptToKotlinBiFunctionType(functionType);
}
else if (KotlinDelegate.isKotlinSupplier(resolvableClass)) {
functionArgumentResolvableType = KotlinDelegate.adaptToKotlinSupplierType(functionType);
}
}
if (functionArgumentResolvableType == ResolvableType.NONE) {
throw new IllegalArgumentException(
"Type must be a Function, BiFunction, Function1 or Function2. Found: " + functionType);
}
return functionArgumentResolvableType.getGeneric(argumentIndex);
}
private static final class KotlinDelegate {
public static boolean isKotlinSupplier(Class<?> clazz) {
return Function0.class.isAssignableFrom(clazz);
}
public static ResolvableType adaptToKotlinSupplierType(ResolvableType resolvableType) {
return resolvableType.as(Function0.class);
}
public static boolean isKotlinFunction(Class<?> clazz) {
return Function1.class.isAssignableFrom(clazz);View on GitHub (pinned to 98a7beda4f)