oracle/graal · error · IllegalArgumentException
Must not call isAutomatic() on an unnamed module
Error message
Must not call isAutomatic() on an unnamed module
What it means
EspressoExternalResolvedJavaModule.isAutomatic() throws when called on a module whose name is null, i.e. an unnamed module. The JVMCI contract for ResolvedJavaModule states isAutomatic() is only valid for named modules, and this Espresso-backed implementation enforces that by checking isNamed() first. The descriptor lookup (java.lang.Module.getDescriptor) would itself fail or be meaningless for an unnamed module, so the call is rejected up front.
Source
Thrown at espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedJavaModule.java:111
return access.java_lang_Module_isExported_String_Module.getMirror().execute(moduleValue, packageNameValue, espressoModule.moduleValue).asBoolean();
}
@Override
public Set<String> getPackages() {
Value packages = access.java_lang_Module_getPackages.getMirror().execute(moduleValue);
return packages.as(new TypeLiteral<>() {
});
}
@Override
public boolean isNamed() {
return name != null;
}
@Override
public boolean isAutomatic() {
if (!isNamed()) {
throw new IllegalArgumentException("Must not call isAutomatic() on an unnamed module");
}
Value moduleDescriptor = access.java_lang_Module_getDescriptor.getMirror().execute(moduleValue);
Value isAutomatic = access.java_lang_module_ModuleDescriptor_isAutomatic.getMirror().execute(moduleDescriptor);
return isAutomatic.asBoolean();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
EspressoExternalResolvedJavaModule that = (EspressoExternalResolvedJavaModule) o;
return moduleValue.equals(that.moduleValue);
}
View on GitHub (pinned to a66e9ccd1d)
Solutions
- Call module.isNamed() before calling isAutomatic() and treat unnamed modules as non-automatic (return false in your wrapper).
- Verify the guest VM is launched with a module system if your code assumes named modules (check the Espresso launch configuration).
- Audit any framework code (annotation processors, service-loader scanning) that unconditionally queries isAutomatic() on classes from the unnamed module.
Example fix
// before boolean auto = module.isAutomatic(); // after boolean auto = module.isNamed() && module.isAutomatic();
Defensive patterns
Strategy: type-guard
Validate before calling
if (!module.isNamed()) {
// treat as non-automatic; skip descriptor queries
return false;
} Type guard
boolean canQueryDescriptor(ResolvedJavaModule m) { return m.isNamed(); } Try / catch
catch (IllegalArgumentException e) only as a last resort when third-party code calls isAutomatic() unconditionally; log and default to false.
Prevention
- Always pair isNamed() checks before any of isAutomatic()/getName()-dependent logic.
- Wrap module queries in a helper that encodes the unnamed-module policy once.
- Test with classpath-loaded guest classes (unnamed module) in addition to modular ones.
When it happens
Trigger: Calling ResolvedJavaModule.isAutomatic() (directly or via JVMCI/Graal code that queries module descriptors) on a module resolved from a class loaded by the boot/platform or unnamed classloader of the guest Espresso VM — every unnamed module has name == null.
Common situations: Guest code compiled with Espresso where classes sit in the unnamed module (classpath-style loading, modules disabled via --no-modules or legacy mode, or polyglot embedding without module layer setup); Graal compiler phases that probe module-automatic status while walking annotations or services.
Related errors
- Constant has unexpected type {}: {}
- Expected an espresso constant got a {}
- Expected an EspressoExternalResolvedJavaMethod, got {}
- Expected an EspressoExternalResolvedJavaField, got {}
- Invalid component type
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/2db253d6e625af11.
Report an issue: GitHub.