HMCL-dev/HMCL · critical · IOException

Self verification failed

Error message

Self verification failed

What it means

UpdateChecker.checkUpdate first verifies the running HMCL binary's integrity via IntegrityChecker.isSelfVerified() (unless checks are disabled). If the current jar fails self-verification (its hash does not match the embedded expected hash/signature), it throws IOException("Self verification failed") and refuses to query update servers. This prevents a tampered binary from fetching or trusting update metadata.

Solutions

  1. Re-download HMCL from the official site/GitHub releases and replace the current jar
  2. If self-built, this is expected — local builds fail the embedded hash check by design
  3. Verify the jar hash against the published checksum
  4. Run with the self-integrity-check disabled flag only if you fully trust the binary (not recommended)

Example fix

// before
if (!IntegrityChecker.DISABLE_SELF_INTEGRITY_CHECK && !IntegrityChecker.isSelfVerified()) {
    throw new IOException("Self verification failed");
}
// after
// user-side: re-download the official jar; do not bypass.
// diagnostic:
LOG.warning("Self verification failed for " + IntegrityChecker.currentJarPath());
throw new IOException("Self verification failed: the HMCL jar was modified; "
    + "please download the official release");
Defensive patterns

Strategy: validation

Validate before calling

// before enabling update checks
if (!IntegrityChecker.DISABLE_SELF_INTEGRITY_CHECK && !IntegrityChecker.isSelfVerified()) {
    // refuse to proceed; prompt the user to re-download the official jar
    Controllers.dialog("HMCL binary integrity check failed. Please re-download from the official site.");
    return;
}

Try / catch

try {
    checkForUpdate();
} catch (IOException e) {
    if ("Self verification failed".equals(e.getMessage())) {
        Controllers.dialog("Your HMCL jar was modified or corrupted; download the official release", MessageType.ERROR);
    } else throw e;
}

Prevention

When it happens

Trigger: Requesting an update check while the executing HMCL jar has been modified after release — rebuilt, patched, repackaged, or corrupted in transfer — so isSelfVerified() returns false and the code path at UpdateChecker.java:88 throws.

Common situations: Downloading HMCL from unofficial mirrors or via a corrupted transfer; jars modified by third-party repacks/patches; building HMCL locally without updating the embedded integrity hash; disk corruption of the jar file.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java:88

    public static boolean isOutdated() {
        return outdated.get();
    }

    public static ObservableBooleanValue outdatedProperty() {
        return outdated;
    }

    public static boolean isCheckingUpdate() {
        return checkingUpdate.get();
    }

    public static ReadOnlyBooleanProperty checkingUpdateProperty() {
        return checkingUpdate.getReadOnlyProperty();
    }

    private static RemoteVersion checkUpdate(UpdateChannel channel, boolean preview) throws IOException {
        if (!IntegrityChecker.DISABLE_SELF_INTEGRITY_CHECK && !IntegrityChecker.isSelfVerified()) {
            throw new IOException("Self verification failed");
        }

        var query = new LinkedHashMap<String, String>();
        query.put("version", Metadata.VERSION);
        query.put("channel", preview ? channel.channelName + "-preview" : channel.channelName);

        String url = NetworkUtils.withQuery(Metadata.HMCL_UPDATE_URL, query);
        return RemoteVersion.fetch(channel, preview, url);
    }

    private static boolean isDevelopmentVersion(String version) {
        return version.contains("@") || // eg. @develop@
                version.contains("SNAPSHOT"); // eg. 3.5.SNAPSHOT
    }

    public static void requestCheckUpdate(UpdateChannel channel, boolean preview) {
        Platform.runLater(() -> {
            if (isCheckingUpdate())

View on GitHub (pinned to 24702dc5a0)