{"record":{"id":"2db253d6e625af11","repo":"oracle/graal","slug":"must-not-call-isautomatic-on-an-unnamed-module-2db253","errorCode":null,"errorMessage":"Must not call isAutomatic() on an unnamed module","messagePattern":"Must not call isAutomatic\\(\\) on an unnamed module","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedJavaModule.java","lineNumber":111,"sourceCode":"        return access.java_lang_Module_isExported_String_Module.getMirror().execute(moduleValue, packageNameValue, espressoModule.moduleValue).asBoolean();\n    }\n\n    @Override\n    public Set<String> getPackages() {\n        Value packages = access.java_lang_Module_getPackages.getMirror().execute(moduleValue);\n        return packages.as(new TypeLiteral<>() {\n        });\n    }\n\n    @Override\n    public boolean isNamed() {\n        return name != null;\n    }\n\n    @Override\n    public boolean isAutomatic() {\n        if (!isNamed()) {\n            throw new IllegalArgumentException(\"Must not call isAutomatic() on an unnamed module\");\n        }\n        Value moduleDescriptor = access.java_lang_Module_getDescriptor.getMirror().execute(moduleValue);\n        Value isAutomatic = access.java_lang_module_ModuleDescriptor_isAutomatic.getMirror().execute(moduleDescriptor);\n        return isAutomatic.asBoolean();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n        EspressoExternalResolvedJavaModule that = (EspressoExternalResolvedJavaModule) o;\n        return moduleValue.equals(that.moduleValue);\n    }\n","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedJavaModule.java#L93-L129","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nboolean auto = module.isAutomatic();\n\n// after\nboolean auto = module.isNamed() && module.isAutomatic();","handlingStrategy":"type-guard","validationCode":"if (!module.isNamed()) {\n    // treat as non-automatic; skip descriptor queries\n    return false;\n}","typeGuard":"boolean canQueryDescriptor(ResolvedJavaModule m) { return m.isNamed(); }","tryCatchPattern":"catch (IllegalArgumentException e) only as a last resort when third-party code calls isAutomatic() unconditionally; log and default to false.","preventionTips":["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."],"tags":["jvmci","espresso","graalvm","modules","api-contract"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}