{"record":{"id":"c96f1b174428539d","repo":"elastic/elasticsearch","slug":"method-reference-passed-to-calling-does-not-be","errorCode":null,"errorMessage":"Method reference passed to 'calling()' does not belong to {} or one of its subclasses.","messagePattern":"Method reference passed to 'calling\\(\\)' does not belong to (.+?) or one of its subclasses\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"libs/entitlement/src/main/java/org/elasticsearch/entitlement/rules/ClassMethodBuilder.java","lineNumber":1184,"sourceCode":"                \"Proxy fallback only supports instance method references; no method was invoked on the proxy for \"\n                    + clazz.getName()\n                    + \". If this is a static method reference, resolution must use SerializedLambda.\"\n            );\n        }\n        return methodKeyFromMethod(holder.recordedMethod);\n    }\n\n    private static MethodKey methodKeyFromMethod(Method method) {\n        String className = method.getDeclaringClass().getName().replace(\".\", \"/\");\n        String methodName = method.getName();\n        List<String> parameterTypes = Arrays.stream(method.getParameterTypes()).map(TypeUtils::getParameterTypeName).toList();\n        return new MethodKey(className, methodName, parameterTypes);\n    }\n\n    private static void assertImplementationClass(Class<?> clazz, String implClassName) throws ClassNotFoundException {\n        Class<?> implClass = Class.forName(implClassName.replace(\"/\", \".\"));\n        if (implClass.isAssignableFrom(clazz) == false) {\n            throw new IllegalArgumentException(\n                \"Method reference passed to 'calling()' does not belong to \" + clazz.getName() + \" or one of its subclasses.\"\n            );\n        }\n    }\n\n    @SuppressForbidden(reason = \"relies on reflection\")\n    private static Class<?> resolveDeclaringClass(Class<?> clazz, String methodName, Class<?>... args) throws NoSuchMethodException {\n        if (\"<init>\".equals(methodName)) {\n            return clazz;\n        }\n        Class<?>[] resolvedArgs = Arrays.stream(args).map(TypeUtils::toPrimitive).toArray(Class[]::new);\n        return clazz.getMethod(methodName, resolvedArgs).getDeclaringClass();\n    }\n\n    @SuppressForbidden(reason = \"relies on reflection\")\n    private static void validateConstructorExists(Class<?> clazz, Class<?>... args) {\n        Class<?>[] resolvedArgs = Arrays.stream(args).map(TypeUtils::toPrimitive).toArray(Class[]::new);\n        try {","sourceCodeStart":1166,"sourceCodeEnd":1202,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/entitlement/src/main/java/org/elasticsearch/entitlement/rules/ClassMethodBuilder.java#L1166-L1202","documentation":"Thrown by ClassMethodBuilder.assertImplementationClass when a method reference passed to the 'calling()' entitlement builder does not belong to the expected class or a subclass. The check loads the implClassName and verifies clazz is assignable from it; if not, the rule was built against the wrong type and would never match at runtime.","triggerScenarios":"assertImplementationClass(clazz, implClassName) calls Class.forName(implClassName) then implClass.isAssignableFrom(clazz) returns false. This happens when the method reference's declaring class is unrelated to the class the rule targets.","commonSituations":"Copy-pasting an entitlement rule and forgetting to update the target class; passing a helper class's method reference where the rule expects the target class; refactor renamed a class but the entitlement rule was not updated.","solutions":["Update the method reference so it belongs to the class named in the error or one of its subclasses.","Re-check the entitlement rule definition and align the target class with the method reference's declaring class.","If the class was renamed, update the rule to reference the new name."],"exampleFix":"// before: rule targets ClassA but reference is from ClassB\nbuilder.calling(ClassB::doWork);\n\n// after: align the reference to the rule's target class\nbuilder.calling(ClassA::doWork);","handlingStrategy":"type-guard","validationCode":"// Validate the declaring class before registering the rule\nClass<?> expected = Class.forName(implClassName.replace(\"/\", \".\"));\nif (!expected.isAssignableFrom(declaredClass)) {\n  throw new IllegalArgumentException(\"method reference class mismatch\");\n}","typeGuard":"boolean ruleClassMatches(Class<?> ruleTarget, Class<?> refDeclaring) {\n  return ruleTarget.isAssignableFrom(refDeclaring);\n}","tryCatchPattern":null,"preventionTips":["When refactoring/renaming a class, update every entitlement rule that references it.","Add a unit test asserting each rule's method reference declares on the expected class."],"tags":["entitlement","method-reference","type-check","policy"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T11:17:21.771Z"}