oracle/graal · error · IllegalArgumentException

${targetModule} can't access ${interfaceName}

Error message

${targetModule} can't access ${interfaceName}

What it means

Thrown by EspressoForeignProxyGenerator's module-access validation: when the proxy must live in targetModule (the module of a package-private interface), that module must be able to read every other proxied interface's module and the interface's package must be open or unqualifiedly exported to it. Failing that, the generator throws '<module> can't access <interface>'. This mirrors java.lang.reflect.Proxy's module checks (Proxy.getProxyClass / Module.addReads semantics).

Source

Thrown at espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/EspressoForeignProxyGenerator.java:381

                    // 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());
                }
            }
            // return the module of the package-private interface
            return new ProxyClassContext(targetModule, targetPackage, 0);
        }

        // All proxy interfaces are public. So maps to a dynamic proxy module
        // and add reads edge and qualified exports, if necessary
        ModuleTable.ModuleEntry targetModule = getDynamicModule(context.getBindingsLoader());

        // set up proxy class access to proxy interfaces and types
        // referenced in the method signature
        Set<Klass> types = new HashSet<>(Arrays.asList(interfaces));
        types.add(superKlass);
        types.addAll(refTypes);
        for (Klass c : types) {
            ensureAccess(targetModule, c);
        }

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Add the needed reads/opens: put 'requires <ifaceModule>;' plus have the interface's module 'exports' (or 'opens') its package, or start the embedding with --add-opens <ifaceModule>/<pkg>=<targetModule>.
  2. Make all proxied interfaces public in exported packages so the generator uses its dynamic proxy module instead (it wires reads/exports automatically).
  3. Proxy only the interfaces the target module can legally access; proxy the rest separately.

Example fix

# before: run without opens -> "module M1 can't access pkg.Iface"
java -m app

# after
java --add-opens M2/pkg.to.proxy=M1 -m app
Defensive patterns

Strategy: validation

Validate before calling

// ensure targetModule can read and access every other interface's module/package
for (ObjectKlass intf : interfaces) {
    ModuleTable.ModuleEntry m = intf.module();
    if (m != targetModule &&
            (!targetModule.canRead(m, isJavaBase) || (!m.isOpen() && !intf.packageEntry().isUnqualifiedExported()))) {
        throw new IllegalArgumentException(targetModule + " cannot access " + intf.getName());
    }
}

Try / catch

try {
    generator.getProxy(context, loader, interfaces);
} catch (IllegalArgumentException e) {
    // module access failure: add --add-opens/--add-exports or make interfaces public, then retry
}

Prevention

When it happens

Trigger: Mixing a package-private interface from module M1 with a public interface from module M2 where M1 does not read M2, or M2 does not export/open the interface's package to M1; JPMS deployments with strong encapsulation.

Common situations: Java modular applications (module-info.java) where a non-exported SPI package is proxied; upgrading a library to explicit modules so previously-reflective access is now encapsulated; missing --add-opens/--add-exports flags at startup.

Related errors


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