HMCL-dev/HMCL · error · PatchException

Failed to add dependencies to classpath!

Error message

Failed to add dependencies to classpath!

What it means

In patch(), after obtaining JavaFX dependencies, HMCL adds them to the running classpath via reflection (loadFromCache). If that reflection fails (ReflectiveOperationException) or the loaded classes are absent (NoClassDefFoundError), it throws this PatchException indicating the JavaFX modules could not be attached to the classpath.

Solutions

  1. Add JVM flags to open the needed modules/packages (--add-opens java.base/jdk.internal.loader=ALL-UNNAMED etc.) or downgrade to a JDK where the reflection is permitted
  2. Use a JDK bundled with JavaFX so classpath patching never runs
  3. Re-download to get a complete, consistent JavaFX module set (clear cache first)
  4. Match JavaFX version to the running JDK architecture/version

Example fix

// before
java -jar HMCL.jar  # reflection into classloader blocked on JDK 17+
// after
java --add-opens java.base/jdk.internal.loader=ALL-UNNAMED -jar HMCL.jar
// or use a JavaFX-bundled JDK
/path/to/zulu-fx-jdk/bin/java -jar HMCL.jar
Defensive patterns

Strategy: try-catch

Validate before calling

// detect early whether classloader reflection will be permitted
try {
    ClassLoader cl = ClassLoader.getSystemClassLoader();
    cl.getClass().getMethod("addURL", URL.class).setAccessible(true);
} catch (ReflectiveOperationException e) {
    LOG.warning("Classpath patching unavailable on this JVM; use a JavaFX-bundled JDK");
}

Try / catch

try {
    SelfDependencyPatcher.patchIfNeeded();
} catch (PatchException e) {
    if (e.getMessage().contains("classpath")) {
        LOG.severe("Could not attach JavaFX to classpath; add --add-opens flags or switch to a JavaFX-bundled JDK", e);
    }
}

Prevention

When it happens

Trigger: patch() calls loadFromCache() which throws when reflecting into the system classloader / URLClassLoader internals fails, or a loaded JavaFX class triggers NoClassDefFoundError (incomplete or mismatched JavaFX module set).

Common situations: Running on a JVM where URLClassLoader addURL/reflection is blocked (strong encapsulation on JDK 16+, SecurityManager); partial JavaFX download causing NoClassDefFoundError; mismatched JavaFX version versus JDK.

Related errors


AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10). Data as JSON: /api/errors/c0efc7b4abdd10b7. Report an issue: GitHub.

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/util/SelfDependencyPatcher.java:192

        LOG.info("Missing JavaFX dependencies, attempting to patch in missing classes");

        // Download missing dependencies
        List<DependencyDescriptor> missingDependencies = patcher.checkMissingDependencies();
        if (!missingDependencies.isEmpty()) {
            try {
                patcher.fetchDependencies(missingDependencies);
            } catch (IOException e) {
                throw new PatchException("Failed to download dependencies", e);
            }
        }

        // Add the dependencies
        try {
            patcher.loadFromCache();
        } catch (IOException ex) {
            throw new PatchException("Failed to load JavaFX cache", ex);
        } catch (ReflectiveOperationException | NoClassDefFoundError ex) {
            throw new PatchException("Failed to add dependencies to classpath!", ex);
        }
        LOG.info(" - Done!");
    }

    private Repository showChooseRepositoryDialog() {
        final JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

        for (String line : i18n("repositories.chooser").split("\n")) {
            panel.add(new JLabel(line));
        }

        final ButtonGroup buttonGroup = new ButtonGroup();

        for (Repository repository : repositories) {
            final JRadioButton button = new JRadioButton(repository.name);
            button.putClientProperty("repository", repository);
            buttonGroup.add(button);

View on GitHub (pinned to 24702dc5a0)