oracle/graal · critical · IllegalArgumentException
Package %s in more than one module
Error message
Package %s in more than one module
What it means
HostVMAccessClassLoader builds a module layer from the user-supplied module path (VMAccess.Builder#modulePath) and, like jdk.internal.loader.Loader, builds a package->module map. The JPMS configuration forbids the same package appearing in two modules in one layer, so if two resolved modules both declare a package, construction fails with this IllegalArgumentException. This is the classic JPMS 'split package' error surfaced at image-build/VMAccess startup.
Source
Thrown at compiler/src/jdk.graal.compiler.hostvmaccess/src/jdk/graal/compiler/hostvmaccess/HostVMAccessClassLoader.java:160
*/
HostVMAccessClassLoader(List<Path> classpath, Configuration configuration, ClassLoader parent) {
super(parent);
Objects.requireNonNull(parent);
this.parent = parent;
// Checkstyle: stop stable iteration order check
Map<String, ModuleReference> nameToModule = new HashMap<>();
Map<String, LoadedModule> packageToModule = new HashMap<>();
// Checkstyle: resume stable iteration order check
for (ResolvedModule resolvedModule : configuration.modules()) {
ModuleReference mref = resolvedModule.reference();
ModuleDescriptor descriptor = mref.descriptor();
nameToModule.put(descriptor.name(), mref);
descriptor.packages().forEach(pn -> {
LoadedModule lm = new LoadedModule(mref);
if (packageToModule.put(pn, lm) != null) {
throw new IllegalArgumentException("Package " + pn + " in more than one module");
}
});
}
localNameToModule = Collections.unmodifiableMap(nameToModule);
localPackageToModule = Collections.unmodifiableMap(packageToModule);
/*
* Unlike {@code jdk.internal.loader.Loader}, we initialize remotePackageToLoader here which
* allows us to use an unmodifiable map instead of a ConcurrentHashMap.
*/
remotePackageToLoader = initRemotePackageMap(configuration, List.of(ModuleLayer.boot()));
/* The only map that gets updated concurrently during the lifetime of this loader. */
moduleToReader = new ConcurrentHashMap<>();
/* Initialize URLClassPath that is used to lookup classes from class-path. */
ucp = new URLClassPath(classpath.stream().map(HostVMAccessClassLoader::toURL).toArray(URL[]::new), null);
}View on GitHub (pinned to a66e9ccd1d)
Solutions
- Run 'jdeps --module-path ... --print-module-deps' or java --describe-module on each module to find which two declare the conflicting package, then remove or rename one
- If the duplicate comes from a shaded/uber module, exclude the overlapping dependency so classes ship once
- Move the offending classes to a distinct package name in one of the modules
- As a last resort, put the artifact only on the class path (unnamed module) instead of the module path, since split packages between class path and module path are tolerated
Example fix
# before --module-path app.jar:legacy-util.jar # both contain com.acme.util # after --module-path app.jar # com.acme.util kept only in app.jar # or relocate: com.acme.util -> com.acme.legacyutil in legacy-util.jar
Defensive patterns
Strategy: validation
Validate before calling
Configuration cf = Configuration.resolve(roots, List.of(ModuleLayer.boot().configuration()), ModuleFinder.of(modulePaths));
// pre-detect split packages before constructing VMAccess
Map<String, String> pkgToModule = new HashMap<>();
for (ResolvedModule rm : cf.modules()) {
for (String pn : rm.reference().descriptor().packages()) {
String prev = pkgToModule.putIfAbsent(pn, rm.name());
if (prev != null) throw new IllegalArgumentException("Split package " + pn + ": " + prev + " vs " + rm.name());
}
} Try / catch
catch (IllegalArgumentException e) { if (e.getMessage().contains("in more than one module")) { print both module names from the message; fix the module path; } } Prevention
- Run jdeps over the module path before builds to catch split packages early
- Keep each library on exactly one path (module path XOR class path)
- Beware automatic modules: their packages are derived from the jar contents and easily overlap
When it happens
Trigger: Two modules on the module path (or pulled in transitively via requires) whose module descriptors both list the same package, e.g. an app module and a library module both containing com.acme.util. Thrown from the HostVMAccessClassLoader constructor while iterating configuration.modules().
Common situations: Duplicating a library as both a module and a jar patch; automatic modules that overlap with named modules (e.g. the same artifact on the class path AND module path in overlapping packages); fat/uber module re-bundling classes that also ship in a dependency module.
Related errors
- Package %s cannot be imported from multiple loaders
- Could not load Graal NodeClass TYPE field for
- LogFile substitution %s cannot be combined with any other ch
- Set the JVMCI_VERSION_CHECK environment variable to "ignore"
- Unknown argument: %s
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/f406e6f4f20ce78b.
Report an issue: GitHub.