spring-projects/spring-ai · error · IllegalArgumentException
ToolContext is required by the method as an argument
Error message
ToolContext is required by the method as an argument
What it means
MethodToolCallback.call() validates that if the target @Tool method declares a ToolContext parameter, a non-empty ToolContext was actually provided at call time. If the method accepts ToolContext but the caller passed null or an empty context, it throws an IllegalArgumentException, because the tool would otherwise run without required contextual data.
Solutions
- Pass a non-empty ToolContext when calling: new ToolCallback invocation with .toolContext(Map.of("key", value)) or methodContext on ChatClient.
- If context is genuinely optional, remove the ToolContext parameter from the @Tool method and use closure/field state instead.
- Guard call sites with a check that the context map is non-empty before invoking the tool.
Example fix
// before
String result = toolCallback.call("{\"q\":\"hi\"}"); // no context, method requires ToolContext
// after
String result = toolCallback.call("{\"q\":\"hi\"}", new ToolContext(Map.of("tenantId", "acme"))); Defensive patterns
Strategy: validation
Validate before calling
if (toolContext == null || toolContext.getContext().isEmpty()) {
throw new IllegalArgumentException("This tool requires a non-empty ToolContext");
} Type guard
boolean contextSatisfied = toolContext != null && !toolContext.getContext().isEmpty();
Try / catch
try { result = toolCallback.call(input, toolContext); } catch (IllegalArgumentException e) { if (e.getMessage().contains("ToolContext is required")) { toolContext = new ToolContext(defaultContext()); result = toolCallback.call(input, toolContext); } else throw e; } Prevention
- Always pass .toolContext(...) at ChatClient call sites whose tools declare ToolContext parameters.
- Document required context keys alongside each @Tool method.
- Add integration tests invoking every tool with representative context.
When it happens
Trigger: Invoking toolCallbacks with a null toolContext or new ToolContext(Map.of()) when the @Tool method signature is e.g. void run(String q, ToolContext ctx); calling the tool directly via chatClient tools without passing context.
Common situations: Tools designed for tenant/user IDs passed via ToolContext being invoked from tests or from ChatClient calls that omit .toolContext(...); framework-internal calls that never supply context.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Anthropic Citations API requires all documents to have…
- textContent and resource cannot be set at the same time
- textContent and resource cannot be set at the same time
- Anthropic cache breakpoint limit (4) reached. Additional…
- argumentConsumer is required
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/618a310ad70b2050.
Report an issue: GitHub.
Appendix: source
Thrown at spring-ai-model/src/main/java/org/springframework/ai/tool/method/MethodToolCallback.java:130
Object[] methodArguments = this.buildMethodArguments(toolArguments, toolContext);
Object result = this.callMethod(methodArguments);
if (logger.isDebugEnabled()) {
logger.debug("Successful execution of tool: " + this.toolDefinition.name());
}
Type returnType = this.toolMethod.getGenericReturnType();
return this.toolCallResultConverter.convert(result, returnType);
}
private void validateToolContextSupport(@Nullable ToolContext toolContext) {
var isNonEmptyToolContextProvided = toolContext != null && !CollectionUtils.isEmpty(toolContext.getContext());
var isToolContextAcceptedByMethod = Stream.of(this.toolMethod.getParameterTypes())
.anyMatch(type -> ClassUtils.isAssignable(ToolContext.class, type));
if (isToolContextAcceptedByMethod && !isNonEmptyToolContextProvided) {
throw new IllegalArgumentException("ToolContext is required by the method as an argument");
}
}
private @Nullable Map<String, Object> extractToolArguments(String toolInput) {
try {
return jsonHelper.fromJson(toolInput, new ParameterizedTypeReference<>() {
});
}
catch (Exception ex) {
logger.warn("Conversion from JSON failed", ex);
Throwable cause = (ex.getCause() instanceof JacksonException) ? ex.getCause() : ex;
throw new ToolExecutionException(this.getToolDefinition(), cause);
}
}
// Based on the implementation in MethodToolCallback.
@SuppressWarnings("null")
private Object[] buildMethodArguments(Map<String, Object> toolInputArguments, @Nullable ToolContext toolContext) {View on GitHub (pinned to 98a7beda4f)