HMCL-dev/HMCL · error · IOException

Malformed response

Error message

Malformed response

What it means

RemoteVersion.fetch parses the update server's JSON with Gson; if the body is not valid JSON, JsonParseException is caught and rethrown as IOException("Malformed response", e). It means the update endpoint answered, but with content that cannot be parsed as JSON (HTML error page, truncation, wrong content-type).

Solutions

  1. Inspect the chained cause (e.getCause()) message to see the actual Gson parse error
  2. Check network/proxy: disable intercepting proxies or complete captive-portal login, then retry
  3. Verify the update URL responds with valid JSON (curl the endpoint)
  4. Skip the in-app update check and update HMCL manually

Example fix

// before
} catch (JsonParseException e) {
    throw new IOException("Malformed response", e);
}
// after
} catch (JsonParseException | IllegalStateException e) {
    throw new IOException("Malformed response from " + url
        + ": " + StringUtils.truncate(rawBody, 200), e);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// validate response body before parsing
HttpResponse<String> resp = client.send(req, ofString());
String body = resp.body().trim();
if (!body.startsWith("{")) {
    throw new IOException("Update endpoint returned non-JSON body (HTTP " + resp.statusCode() + ")");
}

Try / catch

try {
    RemoteVersion v = RemoteVersion.fetch(channel, version, preview);
} catch (IOException e) {
    if ("Malformed response".equals(e.getMessage())) {
        LOG.warning("Update server returned non-JSON; check proxy/network", e.getCause());
        skipUpdateCheck();
    } else throw e;
}

Prevention

When it happens

Trigger: Checking for updates when the metadata endpoint returns non-JSON content — a captive-portal/HTML error page, a 200 response with garbage body, truncated transfer, or a proxy injecting content.

Common situations: Corporate/campus networks with interception proxies; Wi-Fi login portals redirecting requests; CDN or mirror returning an error page with HTTP 200; DNS hijacking; offline-cache corruption.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java:47

import java.util.Optional;

public record RemoteVersion(UpdateChannel channel, String version, String url, Type type, IntegrityCheck integrityCheck,
                            boolean preview, boolean force) {

    public static RemoteVersion fetch(UpdateChannel channel, boolean preview, String url) throws IOException {
        try {
            JsonObject response = JsonUtils.fromNonNullJson(NetworkUtils.doGet(url), JsonObject.class);
            String version = Optional.ofNullable(response.get("version")).map(JsonElement::getAsString).orElseThrow(() -> new IOException("version is missing"));
            String jarUrl = Optional.ofNullable(response.get("jar")).map(JsonElement::getAsString).orElse(null);
            String jarHash = Optional.ofNullable(response.get("jarsha1")).map(JsonElement::getAsString).orElse(null);
            boolean force = Optional.ofNullable(response.get("force")).map(JsonElement::getAsBoolean).orElse(false);
            if (jarUrl != null && jarHash != null) {
                return new RemoteVersion(channel, version, jarUrl, Type.JAR, new IntegrityCheck("SHA-1", jarHash), preview, force);
            } else {
                throw new IOException("No download url is available");
            }
        } catch (JsonParseException e) {
            throw new IOException("Malformed response", e);
        }
    }

    @Override
    public @NotNull String toString() {
        return "[" + version + " from " + url + "]";
    }

    public enum Type {
        JAR
    }
}

View on GitHub (pinned to 24702dc5a0)