oracle/graal · error · IllegalArgumentException
cannot have non-public interfaces in different packages
Error message
cannot have non-public interfaces in different packages
What it means
Thrown by EspressoForeignProxyGenerator while assembling the proxy class context: when the interfaces being proxied include non-public ones, all of them must live in the same runtime package (same package name AND same module). If two package-private interfaces come from different modules or different packages, configuration aborts with this IllegalArgumentException. It replicates java.lang.reflect.Proxy's rule for non-public interfaces.
Source
Thrown at espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/EspressoForeignProxyGenerator.java:359
nonExported = true;
}
}
}
if (!packagePrivateTypes.isEmpty()) {
// 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;View on GitHub (pinned to a66e9ccd1d)
Solutions
- Make the non-public interfaces public so they route to the dynamic proxy module path.
- Restrict the proxy to package-private interfaces that all live in the same package and module.
- Split the proxy into two proxies: one for the package-private group, one for the rest, and bridge them.
- Eliminate split packages so all interfaces in a package come from one module.
Example fix
// before (two package-private interfaces from different packages) Object proxy = proxyGenerator.getProxy(ctx, klassLoader, ifaceA /* pkg p1 */, ifaceB /* pkg p2 */); // after: proxy them separately or make one public Object proxyA = proxyGenerator.getProxy(ctx, klassLoader, ifaceA); Object proxyB = proxyGenerator.getProxy(ctx, klassLoader, ifaceB);
Defensive patterns
Strategy: validation
Validate before calling
// all package-private interfaces must share package AND module
Set<String> pkgs = new HashSet<>();
Set<Object> mods = new HashSet<>();
for (ObjectKlass k : packagePrivateInterfaces) {
pkgs.add(k.packageEntry().packageName());
mods.add(k.module());
}
if (pkgs.size() > 1 || mods.size() > 1) {
throw new IllegalArgumentException("non-public interfaces span multiple packages/modules");
} Try / catch
try {
generator.getProxy(context, loader, interfaces);
} catch (IllegalArgumentException e) {
// message says 'different packages': split the proxy or make interfaces public
} Prevention
- Prefer public interfaces for anything you plan to proxy cross-module.
- Keep package-private interfaces you proxy together in one package and module.
- Avoid split packages across jars.
When it happens
Trigger: Calling the Espresso proxy generator (proxying guest interfaces, e.g. via Polyglot proxies or lambdametafactory-style requests) with a mix of package-private interfaces from different packages or different modules; combining a package-private interface with a public one from another module is fine, but two private ones must agree.
Common situations: Host code implementing multiple guest SPI interfaces that happen to be package-private; guest interfaces in split packages across jars; migrating a codebase where an interface's visibility was reduced from public to package-private, silently breaking proxying combinations.
Related errors
- non-public interface is not defined by the given loader
- ${targetModule} can't access ${interfaceName}
- methods with same signature {} but incompatible return types
- methods with same signature {} but incompatible return types
- Must not call isAutomatic() on an unnamed module
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/1db071e9f9638c7d.
Report an issue: GitHub.