quarkusio/quarkus · error · IllegalStateException
Module of jlink image launcher is not in a module layer
Error message
Module of jlink image launcher is not in a module layer
What it means
After confirming module mode, JLinkAppLauncher.run retrieves the ModuleLayer of the launcher's module via myModule.getLayer(). A null layer means the module was defined but not bound to any module layer (e.g. loaded reflectively or via a non-standard boot), so ModuleLoader.forLayer cannot enumerate/load modules and the launcher aborts.
Source
Thrown at extensions/packaging/jlink/launcher/src/main/java/io/quarkus/jlink/launcher/JLinkAppLauncher.java:32
* The application launcher for a jlink'd module application.
*/
public final class JLinkAppLauncher {
private JLinkAppLauncher() {
}
/**
* Launch the application.
*
* @param args the application arguments
*/
public static void run(String appModule, String[] args) {
Module myModule = JLinkAppLauncher.class.getModule();
if (myModule == null) {
throw new IllegalStateException("Must launch jlink image in module mode only");
}
ModuleLayer myLayer = myModule.getLayer();
if (myLayer == null) {
throw new IllegalStateException("Module of jlink image launcher is not in a module layer");
}
@SuppressWarnings("resource")
ModuleLoader base = ModuleLoader.forLayer("base", myLayer);
// try to ascertain our own path
String cmd = ProcessHandle.current().info().command()
.orElseThrow(() -> new IllegalStateException("Cannot determine image path (ProcessHandle)"));
Path cmdPath = Path.of(cmd);
Path binPath = cmdPath.getParent();
if (binPath == null || !binPath.getFileName().toString().equals("bin")) {
throw new IllegalStateException("Cannot determine image path (bin path)");
}
Path imagePath = binPath.getParent();
if (imagePath == null) {
throw new IllegalStateException("Cannot determine image path (image path)");
}
View on GitHub (pinned to e1c734241f)
Solutions
- Launch the application normally through the generated jlink image launcher so the boot layer contains the launcher module
- Do not load JLinkAppLauncher via reflection or custom classloaders outside a ModuleLayer
- Check for agents/tools that redefine modules and disable them for the launcher
- Verify the JVM is started normally (boot layer intact), not via low-level ModuleLayer APIs
Example fix
// before: reflective load breaks layer binding
Class.forName("io.quarkus.jlink.launcher.JLinkAppLauncher")
// after: exec the image launcher
Process p = new ProcessBuilder("image/bin/myapp").start(); Defensive patterns
Strategy: type-guard
Validate before calling
Module m = JLinkAppLauncher.class.getModule();
if (m == null || m.getLayer() == null) {
throw new IllegalStateException("Launcher module is not bound to a ModuleLayer");
} Type guard
boolean hasModuleLayer(Class<?> c) {
Module m = c.getModule();
return m != null && m.getLayer() != null;
} Try / catch
try {
JLinkAppLauncher.run(appModule, args);
} catch (IllegalStateException e) {
if (e.getMessage().contains("not in a module layer")) {
throw new IllegalStateException("Launcher loaded outside a ModuleLayer; start the image normally", e);
}
throw e;
} Prevention
- Do not load the launcher class reflectively or via custom classloaders
- Disable agents/tools that redefine module loading for the launcher JVM
- Test the image launcher by executing the generated script
When it happens
Trigger: The launcher class's module has no associated ModuleLayer — typically when the class is loaded by a custom/reflective loader without layer definition, or before/atypical layer bootstrapping.
Common situations: Embedding the launcher class in a custom framework that defines modules without layers; exotic classloading setups in test harnesses; launching with instrumentation agents that redefine module loading.
Related errors
- Must launch jlink image in module mode only
- Cannot determine image path (bin path)
- Cannot determine image path (image path)
- Application module not found
- You're not expected to use the unnamed module as identifier
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/bc955c25058dfe08.
Report an issue: GitHub.