{"record":{"id":"d41f45de117c5da7","repo":"spring-projects/spring-ai","slug":"could-not-access-method","errorCode":null,"errorMessage":"Could not access method: ","messagePattern":"Could not access method: ","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-ai-model/src/main/java/org/springframework/ai/tool/method/MethodToolCallback.java","lineNumber":189,"sourceCode":"\t\tcatch (Exception ex) {\n\t\t\tlogger.warn(\"Conversion from JSON failed\", ex);\n\t\t\tThrowable cause = (ex.getCause() instanceof JacksonException) ? ex.getCause() : ex;\n\t\t\tthrow new ToolExecutionException(this.getToolDefinition(), cause);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"NullAway\") // ex.getCause() is guaranteed to be non-null\n\tprivate @Nullable Object callMethod(Object[] methodArguments) {\n\t\tif (isObjectNotPublic() || isMethodNotPublic()) {\n\t\t\tthis.toolMethod.setAccessible(true);\n\t\t}\n\n\t\tObject result;\n\t\ttry {\n\t\t\tresult = this.toolMethod.invoke(this.toolObject, methodArguments);\n\t\t}\n\t\tcatch (IllegalAccessException ex) {\n\t\t\tthrow new IllegalStateException(\"Could not access method: \" + ex.getMessage(), ex);\n\t\t}\n\t\tcatch (InvocationTargetException ex) {\n\t\t\tthrow new ToolExecutionException(this.toolDefinition, ex.getCause());\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate boolean isObjectNotPublic() {\n\t\treturn this.toolObject != null && !Modifier.isPublic(this.toolObject.getClass().getModifiers());\n\t}\n\n\tprivate boolean isMethodNotPublic() {\n\t\treturn !Modifier.isPublic(this.toolMethod.getModifiers());\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"MethodToolCallback{\" + \"toolDefinition=\" + this.toolDefinition + \", toolMetadata=\" + this.toolMetadata","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/spring-ai-model/src/main/java/org/springframework/ai/tool/method/MethodToolCallback.java#L171-L207","documentation":"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.","triggerScenarios":"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).","commonSituations":"@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.","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."],"exampleFix":"// before\nclass SecretTools { @Tool(name=\"x\") private String run() {...} }\n// after\npublic class Tools { @Tool(name=\"x\") public String run() {...} }","handlingStrategy":"validation","validationCode":"if (!java.lang.reflect.Modifier.isPublic(toolMethod.getModifiers()) || !Modifier.isPublic(toolMethod.getDeclaringClass().getModifiers())) {\n    throw new IllegalArgumentException(\"@Tool methods must be public on a public class\");\n}","typeGuard":"static boolean isReflectivelyAccessible(Method m) { return Modifier.isPublic(m.getModifiers()) && Modifier.isPublic(m.getDeclaringClass().getModifiers()); }","tryCatchPattern":"try { result = toolCallback.call(input); } catch (IllegalStateException e) { if (e.getMessage().startsWith(\"Could not access method\")) throw new ToolAccessException(e); throw e; }","preventionTips":["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."],"tags":["reflection","accessibility","java-modules","spring-ai"],"backgroundTag":"method-not-implemented","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"}