{"record":{"id":"06d1468302711c16","repo":"oracle/graal","slug":"non-public-interface-is-not-defined-by-the-given-l","errorCode":null,"errorMessage":"non-public interface is not defined by the given loader","messagePattern":"non-public interface is not defined by the given loader","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/EspressoForeignProxyGenerator.java","lineNumber":365,"sourceCode":"            // all package-private types must be in the same runtime package\n            // i.e. same package name and same module (named or unnamed)\n            //\n            // Configuration will fail if M1 and in M2 defined by the same loader\n            // and both have the same package p (so no need to check class loader)\n            ModuleTable.ModuleEntry targetModule = null;\n            PackageTable.PackageEntry targetPackage = null;\n            for (Map.Entry<ObjectKlass, ModuleTable.ModuleEntry> e : packagePrivateTypes.entrySet()) {\n                PackageTable.PackageEntry currentPackage = e.getKey().packageEntry();\n                ModuleTable.ModuleEntry m = e.getValue();\n                if ((targetModule != null && targetModule != m) ||\n                                (targetPackage != null && !targetPackage.equals(currentPackage))) {\n                    throw new IllegalArgumentException(\n                                    \"cannot have non-public interfaces in different packages\");\n                }\n                if (m.classLoader() != proxyClassLoader) {\n                    // the specified loader is not the same class loader\n                    // of the non-public interface\n                    throw new IllegalArgumentException(\n                                    \"non-public interface is not defined by the given loader\");\n                }\n\n                targetModule = m;\n                targetPackage = currentPackage;\n            }\n\n            // validate if the target module can access all other interfaces\n            for (ObjectKlass intf : interfaces) {\n                ModuleTable.ModuleEntry m = intf.module();\n                if (m == targetModule) {\n                    continue;\n                }\n\n                if (!targetModule.canRead(m, context.isJavaBase(m)) || (!m.isOpen() && !intf.packageEntry().isUnqualifiedExported())) {\n                    throw new IllegalArgumentException(targetModule + \" can't access \" + intf.getName());\n                }\n            }","sourceCodeStart":347,"sourceCodeEnd":383,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/EspressoForeignProxyGenerator.java#L347-L383","documentation":"Thrown by EspressoForeignProxyGenerator when proxying a non-public interface that is not defined by the classloader the proxy class is being created for (m.classLoader() != proxyClassLoader). Java requires the generated proxy for package-private interfaces to be defined by the exact same loader as the interface, so the generator refuses mismatched loaders.","triggerScenarios":"Requesting a proxy in classloader L for a package-private interface loaded by classloader L2 != L; typical in multi-loader guest setups (web-container style isolating loaders, OSGi-like embeddings) or when the caller passes the wrong loader explicitly.","commonSituations":"Polyglot embeddings with custom guest classloaders; passing the system/bindings loader instead of the loader that defined the private interface; classloader redelegation causing the same interface name to be loaded twice by different loaders.","solutions":["Pass the classloader that actually defined the non-public interface: intf.getDefiningLoader() / obtain the loader from the interface's ObjectKlass.","Ensure the interface class is only loaded by one loader (parent-first delegation for that package).","Alternatively make the interface public, removing the same-loader restriction."],"exampleFix":"// before\nClassProxy p = generator.getProxy(context, someOtherLoader, packagePrivateIntf);\n\n// after\nClassLoader defining = packagePrivateIntf.getClassLoader();\nClassProxy p = generator.getProxy(context, defining, packagePrivateIntf);","handlingStrategy":"validation","validationCode":"for (ObjectKlass intf : packagePrivateInterfaces) {\n    if (intf.module().classLoader() != proxyClassLoader) {\n        throw new IllegalArgumentException(\"loader mismatch for \" + intf.getName());\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    generator.getProxy(context, requestedLoader, interfaces);\n} catch (IllegalArgumentException e) {\n    // retry with the defining loader of the non-public interface\n    generator.getProxy(context, packagePrivateIntf.getClassLoader(), interfaces);\n}","preventionTips":["Always proxy with the classloader that defines the non-public interface.","Prevent duplicate loading of the same interface by multiple loaders (parent delegation).","Make interfaces public when proxies must be created by an unrelated loader."],"tags":["proxy","classloader","interoperability","espresso"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}