spring-projects/spring-ai · error · IllegalStateException
Could not access method:
Error message
Could not access method:
What it means
MethodToolCallback.callMethod() invokes the @Tool method reflectively via Method.invoke. If the method is not accessible from the caller (non-public method on a non-public class without setAccessible success), IllegalAccessException is converted into an IllegalStateException with this message.
Source
Thrown at spring-ai-model/src/main/java/org/springframework/ai/tool/method/MethodToolCallback.java:189
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);
}
}
@SuppressWarnings("NullAway") // ex.getCause() is guaranteed to be non-null
private @Nullable Object callMethod(Object[] methodArguments) {
if (isObjectNotPublic() || isMethodNotPublic()) {
this.toolMethod.setAccessible(true);
}
Object result;
try {
result = this.toolMethod.invoke(this.toolObject, methodArguments);
}
catch (IllegalAccessException ex) {
throw new IllegalStateException("Could not access method: " + ex.getMessage(), ex);
}
catch (InvocationTargetException ex) {
throw new ToolExecutionException(this.toolDefinition, ex.getCause());
}
return result;
}
private boolean isObjectNotPublic() {
return this.toolObject != null && !Modifier.isPublic(this.toolObject.getClass().getModifiers());
}
private boolean isMethodNotPublic() {
return !Modifier.isPublic(this.toolMethod.getModifiers());
}
@Override
public String toString() {
return "MethodToolCallback{" + "toolDefinition=" + this.toolDefinition + ", toolMetadata=" + this.toolMetadataView on GitHub (pinned to 98a7beda4f)
Solutions
- Make the @Tool method public on a public class.
- If the class is in a named module, add `opens <package>` to module-info.java so reflection can access it.
- Check the wrapped exception message for IllegalAccess details; verify no proxy hides a non-public target method.
Example fix
// before
class SecretTools { @Tool(name="x") private String run() {...} }
// after
public class Tools { @Tool(name="x") public String run() {...} } Defensive patterns
Strategy: validation
Validate before calling
if (!java.lang.reflect.Modifier.isPublic(toolMethod.getModifiers()) || !Modifier.isPublic(toolMethod.getDeclaringClass().getModifiers())) {
throw new IllegalArgumentException("@Tool methods must be public on a public class");
} Type guard
static boolean isReflectivelyAccessible(Method m) { return Modifier.isPublic(m.getModifiers()) && Modifier.isPublic(m.getDeclaringClass().getModifiers()); } Try / catch
try { result = toolCallback.call(input); } catch (IllegalStateException e) { if (e.getMessage().startsWith("Could not access method")) throw new ToolAccessException(e); throw e; } Prevention
- Declare @Tool methods public on public top-level classes.
- With JPMS, `opens` the tool packages in module-info.java.
- Run tool-registry construction in a smoke test so accessibility failures surface at startup.
When it happens
Trigger: Registering an object whose @Tool method is private/protected and inaccessible via reflection (e.g. defined in a package-private class, module restrictions, or a security manager/JPMS strong encapsulation).
Common situations: @Tool methods on inner or package-private classes; JDK 16+ module strong encapsulation blocking setAccessible; AOP proxies where the target class differs; loading tools from another module without opens.
Related errors
- ClassNotFoundException (wrapped RuntimeException)
- Failed to extract record field types
- Failed to instantiate ToolCallResultConverter:
- Neither createIndex() nor ensureIndex() method found on Inde
- Failed to invoke ensureIndex() method
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/d41f45de117c5da7.
Report an issue: GitHub.