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

  1. Call module.isNamed() before calling isAutomatic() and treat unnamed modules as non-automatic (return false in your wrapper).
  2. Verify the guest VM is launched with a module system if your code assumes named modules (check the Espresso launch configuration).
  3. 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

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


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