{"record":{"id":"ccf412c715337af3","repo":"elastic/elasticsearch","slug":"error-occurred-when-inspecting-class","errorCode":null,"errorMessage":"Error occurred when inspecting class: {}","messagePattern":"Error occurred when inspecting class: (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"libs/entitlement/src/main/java/org/elasticsearch/entitlement/rules/ClassMethodBuilder.java","lineNumber":1110,"sourceCode":"    @SuppressForbidden(reason = \"relies on reflection\")\n    private static MethodKey resolveMethodReference(Class<?> clazz, Object ref, Class<?>... args) {\n        try {\n            return resolveMethodReferenceViaSerializedLambda(clazz, ref, args);\n        } catch (Exception e) {\n            if (clazz.isInterface()) {\n                try {\n                    return resolveMethodReferenceViaProxy(clazz, ref, args);\n                } catch (Exception proxyException) {\n                    proxyException.addSuppressed(e);\n                    throw new RuntimeException(\n                        \"Error occurred when inspecting class: \"\n                            + clazz.getName()\n                            + \"; SerializedLambda resolution failed and proxy fallback failed\",\n                        proxyException\n                    );\n                }\n            }\n            throw new RuntimeException(\"Error occurred when inspecting class: \" + clazz.getName(), e);\n        }\n    }\n\n    @SuppressForbidden(reason = \"relies on reflection\")\n    private static MethodKey resolveMethodReferenceViaSerializedLambda(Class<?> clazz, Object ref, Class<?>... args) throws Exception {\n        Method writeReplace = ref.getClass().getDeclaredMethod(\"writeReplace\");\n        writeReplace.setAccessible(true);\n\n        SerializedLambda serialized = (SerializedLambda) writeReplace.invoke(ref);\n        String className = serialized.getImplClass();\n        String methodName = serialized.getImplMethodName();\n\n        assertImplementationClass(clazz, className);\n\n        return new MethodKey(\n            resolveDeclaringClass(clazz, methodName, args).getTypeName().replace(\".\", \"/\"),\n            methodName,\n            Arrays.stream(args).map(TypeUtils::getParameterTypeName).toList()","sourceCodeStart":1092,"sourceCodeEnd":1128,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/entitlement/src/main/java/org/elasticsearch/entitlement/rules/ClassMethodBuilder.java#L1092-L1128","documentation":"Thrown by ClassMethodBuilder.resolveMethodReference when SerializedLambda resolution fails for a NON-interface class (the proxy-fallback branch only applies to interfaces). The original exception is the cause. This means the method-reference passed to an entitlement rule on a concrete class could not be decoded via the lambda's writeReplace/SerializedLambda mechanism.","triggerScenarios":"resolveMethodReferenceViaSerializedLambda throws for a class where clazz.isInterface() is false; the else branch rethrows wrapped with the class name. Typical when the method reference is not a serializable lambda or writeReplace is inaccessible.","commonSituations":"Passing a non-serializable method reference to calling(); the lambda target type is not Serializable; reflective access to writeReplace is denied by module rules; the reference is actually a reflective Method handle, not a lambda.","solutions":["Make the functional interface the method reference targets extend Serializable so writeReplace is available.","Run with module opens that allow reflective access to the lambda's class (add '--add-opens' if appropriate for the test/tool).","If the reference is not really a lambda, refactor the rule to use an explicit constructor/method overload (e.g. resolveConstructor / validateConstructorExists).","Inspect the cause exception for the specific reflection failure."],"exampleFix":"// before: non-serializable functional interface\npolicy.calling(MyClass::method).isAllowed(...);\n\n// after: serializable interface\ninterface MySerFun extends Runnable, Serializable {}\npolicy.calling((MySerFun) MyClass::method).isAllowed(...);","handlingStrategy":"try-catch","validationCode":"// Ensure the functional interface is Serializable before building the rule\nif (!java.io.Serializable.class.isAssignableFrom(funType)) {\n  throw new IllegalArgumentException(funType + \" must be Serializable for SerializedLambda resolution\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  builder.calling(ref);\n} catch (RuntimeException e) {\n  if (e.getMessage().startsWith(\"Error occurred when inspecting class:\")) {\n    // root cause is in e.getCause(); fix serializability or refactor to explicit method/constructor overloads\n  }\n  throw e;\n}","preventionTips":["Make functional interfaces extend Serializable for non-interface targets.","Use the explicit method-name/constructor overloads when lambda resolution is brittle."],"tags":["entitlement","method-reference","reflection","lambda","policy"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T06:17:24.410Z"}