oracle/graal · error · IllegalArgumentException
non-public interface is not defined by the given loader
Error message
non-public interface is not defined by the given loader
What it means
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.
Source
Thrown at espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/EspressoForeignProxyGenerator.java:365
// all package-private types must be in the same runtime package
// i.e. same package name and same module (named or unnamed)
//
// Configuration will fail if M1 and in M2 defined by the same loader
// and both have the same package p (so no need to check class loader)
ModuleTable.ModuleEntry targetModule = null;
PackageTable.PackageEntry targetPackage = null;
for (Map.Entry<ObjectKlass, ModuleTable.ModuleEntry> e : packagePrivateTypes.entrySet()) {
PackageTable.PackageEntry currentPackage = e.getKey().packageEntry();
ModuleTable.ModuleEntry m = e.getValue();
if ((targetModule != null && targetModule != m) ||
(targetPackage != null && !targetPackage.equals(currentPackage))) {
throw new IllegalArgumentException(
"cannot have non-public interfaces in different packages");
}
if (m.classLoader() != proxyClassLoader) {
// the specified loader is not the same class loader
// of the non-public interface
throw new IllegalArgumentException(
"non-public interface is not defined by the given loader");
}
targetModule = m;
targetPackage = currentPackage;
}
// validate if the target module can access all other interfaces
for (ObjectKlass intf : interfaces) {
ModuleTable.ModuleEntry m = intf.module();
if (m == targetModule) {
continue;
}
if (!targetModule.canRead(m, context.isJavaBase(m)) || (!m.isOpen() && !intf.packageEntry().isUnqualifiedExported())) {
throw new IllegalArgumentException(targetModule + " can't access " + intf.getName());
}
}View on GitHub (pinned to a66e9ccd1d)
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.
Example fix
// before ClassProxy p = generator.getProxy(context, someOtherLoader, packagePrivateIntf); // after ClassLoader defining = packagePrivateIntf.getClassLoader(); ClassProxy p = generator.getProxy(context, defining, packagePrivateIntf);
Defensive patterns
Strategy: validation
Validate before calling
for (ObjectKlass intf : packagePrivateInterfaces) {
if (intf.module().classLoader() != proxyClassLoader) {
throw new IllegalArgumentException("loader mismatch for " + intf.getName());
}
} Try / catch
try {
generator.getProxy(context, requestedLoader, interfaces);
} catch (IllegalArgumentException e) {
// retry with the defining loader of the non-public interface
generator.getProxy(context, packagePrivateIntf.getClassLoader(), interfaces);
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- cannot have non-public interfaces in different packages
- methods with same signature {} but incompatible return types
- methods with same signature {} but incompatible return types
- ${targetModule} can't access ${interfaceName}
- Resource {resource} not found from class loader: {loaderClas
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/06d1468302711c16.
Report an issue: GitHub.