HMCL-dev/HMCL · error · IOException
Malformed response\n" + text
Error message
Malformed response\n" + text
What it means
AuthlibInjectorServer.refreshMetadata parses the server's metadata response body; if JSON parsing fails it wraps the JsonParseException in an IOException "Malformed response\n" + the raw text. This means the remote server did not return valid authlib-injector metadata JSON.
Solutions
- Verify the URL points to a real authlib-injector (Yggdrasil) API root, not a website landing page
- Inspect the raw response text (included in the exception) to see what the server returned
- Retry when behind a captive portal or proxy, or use a different network
- Check the server's status page; the instance may be down or misconfigured
Example fix
// before
server = AuthlibInjectorServer.locateServer("https://example.com/"); // HTML page
// after
server = AuthlibInjectorServer.locateServer("https://auth.server.example/api/yggdrasil"); Defensive patterns
Strategy: try-catch
Validate before calling
// optionally pre-check that the URL responds with JSON
String body = httpGetString(url);
if (body == null || body.isBlank() || !body.strip().startsWith("{")) {
throw new IOException("Server did not return JSON metadata");
} Try / catch
try {
AuthlibInjectorServer server = AuthlibInjectorServer.locateServer(url);
} catch (IOException e) {
ui.showError("Server returned malformed metadata: " + e.getMessage());
} Prevention
- Enter the API root of an authlib-injector/Yggdrasil server, not a website
- Log the response body included in the exception for diagnosis
- Detect captive portals/proxies that return HTML
- Retry metadata refresh on transient server failures
When it happens
Trigger: Calling locateServer/refreshMetadata against a URL whose HTTP response body is not valid JSON (HTML error page, empty body, proxy interstitial, wrong service).
Common situations: Entering a non-authlib-injector URL (landing page or HTML status page); captive portals/proxies returning HTML; server returning 200 with an error page; truncated responses.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- storage does not have API root.
- storage does not have loginName
- GameRemoteVersions.versions cannot be null
- authlib-injectors.json -> urls cannot be null.
- Illegal result:
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/94ffab72c8851d63.
Report an issue: GitHub.
Appendix: source
Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorServer.java:157
}
public String fetchMetadataResponse() throws IOException {
if (metadataResponse == null || !metadataRefreshed) {
refreshMetadata();
}
return getMetadataResponse().get();
}
public void refreshMetadata() throws IOException {
refreshMetadata(HttpRequest.GET(url).getString());
}
private void refreshMetadata(String text) throws IOException {
long timestamp = System.currentTimeMillis();
try {
setMetadataResponse(text, timestamp);
} catch (JsonParseException e) {
throw new IOException("Malformed response\n" + text, e);
}
metadataRefreshed = true;
LOG.info("authlib-injector server metadata refreshed: " + url);
Platform.runLater(helper::invalidate);
}
private void setMetadataResponse(String metadataResponse, long metadataTimestamp) throws JsonParseException {
JsonObject response = GSON.fromJson(metadataResponse, JsonObject.class);
if (response == null) {
throw new JsonParseException("Metadata response is empty");
}
synchronized (this) {
this.metadataResponse = metadataResponse;
this.metadataTimestamp = metadataTimestamp;
Optional<JsonObject> metaObject = tryCast(response.get("meta"), JsonObject.class);View on GitHub (pinned to 24702dc5a0)