{"record":{"id":"7b077f176e425b7d","repo":"oracle/graal","slug":"targetmodule-can-t-access-interfacename","errorCode":null,"errorMessage":"${targetModule} can't access ${interfaceName}","messagePattern":"(.+?) can't access (.+?)","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":381,"sourceCode":"                    // 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            }\n            // return the module of the package-private interface\n            return new ProxyClassContext(targetModule, targetPackage, 0);\n        }\n\n        // All proxy interfaces are public. So maps to a dynamic proxy module\n        // and add reads edge and qualified exports, if necessary\n        ModuleTable.ModuleEntry targetModule = getDynamicModule(context.getBindingsLoader());\n\n        // set up proxy class access to proxy interfaces and types\n        // referenced in the method signature\n        Set<Klass> types = new HashSet<>(Arrays.asList(interfaces));\n        types.add(superKlass);\n        types.addAll(refTypes);\n        for (Klass c : types) {\n            ensureAccess(targetModule, c);\n        }","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/EspressoForeignProxyGenerator.java#L363-L399","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["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>.","Make all proxied interfaces public in exported packages so the generator uses its dynamic proxy module instead (it wires reads/exports automatically).","Proxy only the interfaces the target module can legally access; proxy the rest separately."],"exampleFix":"# before: run without opens -> \"module M1 can't access pkg.Iface\"\njava -m app\n\n# after\njava --add-opens M2/pkg.to.proxy=M1 -m app","handlingStrategy":"validation","validationCode":"// ensure targetModule can read and access every other interface's module/package\nfor (ObjectKlass intf : interfaces) {\n    ModuleTable.ModuleEntry m = intf.module();\n    if (m != targetModule &&\n            (!targetModule.canRead(m, isJavaBase) || (!m.isOpen() && !intf.packageEntry().isUnqualifiedExported()))) {\n        throw new IllegalArgumentException(targetModule + \" cannot access \" + intf.getName());\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    generator.getProxy(context, loader, interfaces);\n} catch (IllegalArgumentException e) {\n    // module access failure: add --add-opens/--add-exports or make interfaces public, then retry\n}","preventionTips":["Export or open packages containing interfaces you proxy across modules.","Prefer all-public interfaces so Espresso's dynamic proxy module handles wiring.","Add required --add-opens flags to documented startup scripts for modular apps."],"tags":["proxy","modules","jpms","encapsulation","espresso"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}