{"record":{"id":"4f56bd711013e659","repo":"oracle/graal","slug":"cannot-invokeinterface-s","errorCode":null,"errorMessage":"cannot invokeinterface %s","messagePattern":"cannot invokeinterface (.+?)","errorType":"exception","errorClass":"IncompatibleClassChangeError","httpStatus":null,"severity":"error","filePath":"compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/classfile/ClassfileConstant.java","lineNumber":148,"sourceCode":"\n        ExecutableRef(byte tag, DataInputStream stream) throws IOException {\n            super(tag, stream);\n        }\n\n        ResolvedJavaMethod resolve(ClassfileConstantPool cp, int opcode) {\n            if (method == null) {\n                ResolvedJavaType cls = cp.get(ClassRef.class, classIndex).resolve(cp);\n                NameAndType nameAndType = cp.get(NameAndType.class, nameAndTypeIndex);\n                String name = nameAndType.getName(cp);\n                String type = nameAndType.getType(cp);\n\n                if (opcode == Bytecodes.INVOKEINTERFACE) {\n                    method = resolveMethod(cp.context, cls, name, type, false);\n                    if (method == null) {\n                        throw new NoSuchMethodError(cls.toJavaName() + \".\" + name + type);\n                    }\n                    if (!method.isPublic() || !(method.getDeclaringClass().isInterface() || method.getDeclaringClass().isJavaLangObject())) {\n                        throw new IncompatibleClassChangeError(\"cannot invokeinterface \" + method.format(\"%H.%n(%P)%R\"));\n                    }\n                } else if (opcode == Bytecodes.INVOKEVIRTUAL || opcode == Bytecodes.INVOKESPECIAL) {\n                    method = resolveMethod(cp.context, cls, name, type, false);\n                    if (method == null) {\n                        throw new NoSuchMethodError(cls.toJavaName() + \".\" + name + type);\n                    }\n                } else {\n                    assert opcode == Bytecodes.INVOKESTATIC : Assertions.errorMessage(opcode, cp);\n                    method = resolveMethod(cp.context, cls, name, type, true);\n                    if (method == null) {\n                        throw new NoSuchMethodError(cls.toJavaName() + \".\" + name + type);\n                    }\n                }\n            }\n            return method;\n        }\n    }\n","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/classfile/ClassfileConstant.java#L130-L166","documentation":"When this custom resolver emulates invokeinterface linkage, it applies the JVM rule that the resolved method must be public and declared in an interface (or java.lang.Object); otherwise it throws IncompatibleClassChangeError('cannot invokeinterface ...'), reproducing the check HotSpot performs at link time.","triggerScenarios":"invokeinterface resolves to a non-public method (e.g. a private interface method, which since JDK 9 must be invoked with invokespecial) or to a method whose declaring class is a class, not an interface (hierarchy changed between compilation and runtime).","commonSituations":"Stale jars on the classpath where a method moved between class and interface; bytecode generators (ASM) emitting INVOKEINTERFACE for static/private interface methods; compiling against one interface version and running against another.","solutions":["Recompile the caller against the interface version actually present at runtime.","If you generate bytecode, emit the correct opcode: INVOKESTATIC for static, INVOKESPECIAL for private interface methods, INVOKEVIRTUAL/INVOKESPECIAL for default-method calls.","Align dependency versions so the compiled hierarchy matches the runtime hierarchy."],"exampleFix":"// ASM bytecode generation\n// before\nmv.visitMethodInsn(Opcodes.INVOKEINTERFACE, \"Iface\", \"staticHelper\", \"()V\", true); // static method -> error\n\n// after\nmv.visitMethodInsn(Opcodes.INVOKESTATIC, \"Iface\", \"staticHelper\", \"()V\", false);","handlingStrategy":"try-catch","validationCode":"// Before linking, check the invokeinterface contract with reflection\nstatic void checkInvokeInterface(Class<?> iface, String name, Class<?>... params) throws NoSuchMethodException {\n    java.lang.reflect.Method m = iface.getMethod(name, params); // getMethod => public only\n    if (!iface.isInterface() && !m.getDeclaringClass().equals(Object.class)) {\n        throw new IncompatibleClassChangeError(\"cannot invokeinterface \" + m);\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    JavaMethod m = pool.lookupMethod(index, opcode);\n} catch (IncompatibleClassChangeError e) {\n    // Recompile the caller against the current interface version, or fix the opcode; not retryable\n    throw new IllegalStateException(\"Interface/class hierarchy mismatch at link time\", e);\n}","preventionTips":["Keep compile-time and runtime versions of interface-bearing jars identical.","When generating bytecode with ASM, choose the opcode from the resolved method's modifiers (static -> INVOKESTATIC, private -> INVOKESPECIAL)."],"tags":["classfile","invokeinterface","linkage","incompatible-class-change","bytecode-generation"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}