quarkusio/quarkus · error · IllegalStateException

Must launch jlink image in module mode only

Error message

Must launch jlink image in module mode only

What it means

JLinkAppLauncher.run asserts the launcher class was loaded as part of a named Module (module mode). When Class.getModule() returns null (or the unnamed module scenario the code treats as absence), the jlink image is being launched outside module mode, which this launcher does not support. The launcher relies on the module layer to load application modules.

Source

Thrown at extensions/packaging/jlink/launcher/src/main/java/io/quarkus/jlink/launcher/JLinkAppLauncher.java:28

import io.smallrye.modules.ModuleFinder;
import io.smallrye.modules.ModuleLoader;

/**
 * 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();

View on GitHub (pinned to e1c734241f)

Solutions

  1. Launch via the generated image launcher script (image/bin/<name>) rather than java -cp
  2. Ensure the main class and module-info are on the module path with `-m <module>/<mainclass>`
  3. Do not extract/repackage the jlink image into a flat classpath jar
  4. Regenerate the image with the jlink packaging extension if launcher scripts were modified

Example fix

// before: runs in classpath (unnamed module) mode
java -cp image/app.jar com.example.Main
// after: module mode via launcher script or -m
image/bin/myapp
// or: java -m com.example/com.example.Main
Defensive patterns

Strategy: validation

Validate before calling

// ensure module mode before launching
Module m = JLinkAppLauncher.class.getModule();
if (m == null || m.getLayer() == null) {
    throw new IllegalStateException("JLink launcher must run in module mode; use image/bin/<launcher>");
}

Type guard

boolean runsInModuleMode(Class<?> c) {
    Module m = c.getModule();
    return m != null && m.isNamed() && m.getLayer() != null;
}

Try / catch

try {
    JLinkAppLauncher.run(appModule, args);
} catch (IllegalStateException e) {
    if (e.getMessage().contains("module mode")) {
        System.err.println("Launch via the image launcher script, not the classpath");
    }
    throw e;
}

Prevention

When it happens

Trigger: Running the generated main class from the classpath instead of via the module-path launcher; running the launcher in an environment without a module system context (e.g. reflective/flat classpath execution).

Common situations: Launching the image's main class directly with `java -cp` instead of the generated `bin/<launcher>` script; repackaging the application as a fat jar and running it; test harnesses loading the class off the classpath.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/3904a6db647f8502. Report an issue: GitHub.