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

  1. Pass the classloader that actually defined the non-public interface: intf.getDefiningLoader() / obtain the loader from the interface's ObjectKlass.
  2. Ensure the interface class is only loaded by one loader (parent-first delegation for that package).
  3. 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

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


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/06d1468302711c16. Report an issue: GitHub.