{"record":{"id":"1db071e9f9638c7d","repo":"oracle/graal","slug":"cannot-have-non-public-interfaces-in-different-pac","errorCode":null,"errorMessage":"cannot have non-public interfaces in different packages","messagePattern":"cannot have non-public interfaces in different packages","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":359,"sourceCode":"                    nonExported = true;\n                }\n            }\n        }\n\n        if (!packagePrivateTypes.isEmpty()) {\n            // all package-private types must be in the same runtime package\n            // i.e. same package name and same module (named or unnamed)\n            //\n            // Configuration will fail if M1 and in M2 defined by the same loader\n            // and both have the same package p (so no need to check class loader)\n            ModuleTable.ModuleEntry targetModule = null;\n            PackageTable.PackageEntry targetPackage = null;\n            for (Map.Entry<ObjectKlass, ModuleTable.ModuleEntry> e : packagePrivateTypes.entrySet()) {\n                PackageTable.PackageEntry currentPackage = e.getKey().packageEntry();\n                ModuleTable.ModuleEntry m = e.getValue();\n                if ((targetModule != null && targetModule != m) ||\n                                (targetPackage != null && !targetPackage.equals(currentPackage))) {\n                    throw new IllegalArgumentException(\n                                    \"cannot have non-public interfaces in different packages\");\n                }\n                if (m.classLoader() != proxyClassLoader) {\n                    // 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;","sourceCodeStart":341,"sourceCodeEnd":377,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/EspressoForeignProxyGenerator.java#L341-L377","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (two package-private interfaces from different packages)\nObject proxy = proxyGenerator.getProxy(ctx, klassLoader, ifaceA /* pkg p1 */, ifaceB /* pkg p2 */);\n\n// after: proxy them separately or make one public\nObject proxyA = proxyGenerator.getProxy(ctx, klassLoader, ifaceA);\nObject proxyB = proxyGenerator.getProxy(ctx, klassLoader, ifaceB);","handlingStrategy":"validation","validationCode":"// all package-private interfaces must share package AND module\nSet<String> pkgs = new HashSet<>();\nSet<Object> mods = new HashSet<>();\nfor (ObjectKlass k : packagePrivateInterfaces) {\n    pkgs.add(k.packageEntry().packageName());\n    mods.add(k.module());\n}\nif (pkgs.size() > 1 || mods.size() > 1) {\n    throw new IllegalArgumentException(\"non-public interfaces span multiple packages/modules\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    generator.getProxy(context, loader, interfaces);\n} catch (IllegalArgumentException e) {\n    // message says 'different packages': split the proxy or make interfaces public\n}","preventionTips":["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."],"tags":["proxy","modules","packages","interoperability","espresso"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}