HMCL-dev/HMCL · error · ServerResponseMalformedException
uhs mismatched
Error message
uhs mismatched
What it means
getUhs extracts the user hash (uhs) from the XSTS displayClaims.xui[0].uhs field. When an existingUhs from a prior authentication is known, the new uhs must match; otherwise ServerResponseMalformedException('uhs mismatched') is thrown, since a different uhs means the tokens now belong to a different Xbox user.
Solutions
- Remove the account from HMCL and log in again so uhs, refresh token, and session are all captured from the same identity
- Ensure only one HMCL instance manages the account file at a time (avoid concurrent writes)
- Verify you are signing in with the same Microsoft account that originally created the entry
- If you intentionally changed accounts, delete the old entry first instead of reusing stored tokens
Example fix
// before: refreshing stale tokens across an account switch
account.logIn();
// after: detect uhs mismatch and force clean re-login
try {
account.logIn();
} catch (ServerResponseMalformedException e) {
if (e.getMessage().contains("uhs mismatched")) {
accounts.removeAccount(account);
accounts.createAccount(Accounts.OAUTH_MICROSOFT).logIn();
} else {
throw e;
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// pre-compare stored uhs if you persist it
String storedUhs = getStoredUhs();
if (storedUhs != null && !storedUhs.equals(freshUhs)) {
scheduleCleanReauth(); // don't reuse old refresh token
} Try / catch
try {
account.logIn();
} catch (ServerResponseMalformedException e) {
if ("uhs mismatched".equals(e.getMessage())) {
accounts.removeAccount(account);
accounts.createAccount(Accounts.OAUTH_MICROSOFT).logIn();
} else throw e;
} Prevention
- Sign in with the same Microsoft account that created the entry
- Avoid running multiple HMCL instances against the same account file
- After relinking Xbox/Microsoft accounts, delete and re-add the HMCL entry
When it happens
Trigger: authenticateViaLiveAccessToken/refresh exchanges tokens with XSTS and the returned uhs differs from the uhs stored on the account; the Microsoft account linked to the Xbox profile changed; stored uhs was written by a different login than the refresh token being used.
Common situations: User switched Microsoft accounts but HMCL reused the old refresh token; Xbox account re-linked to another Microsoft account; concurrent logins from two HMCL instances on different accounts overwrote each other's storage; Xbox profile migrated.
Related errors
- Selected profile changed
- Profile name is missing
- Selected profile changed
- Profile name is missing
- Failed to select character
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/39e3c2d025d8903c.
Report an issue: GitHub.
Appendix: source
Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java:112
} catch (JsonParseException e) {
throw new ServerResponseMalformedException(e);
}
}
private String getUhs(XBoxLiveAuthenticationResponse response, String existingUhs) throws AuthenticationException {
if (response.errorCode != 0) {
throw new XboxAuthorizationException(response.errorCode, response.redirectUrl);
}
if (response.displayClaims == null || response.displayClaims.xui == null || response.displayClaims.xui.size() == 0 || !response.displayClaims.xui.get(0).containsKey("uhs")) {
LOG.warning("Unrecognized xbox authorization response " + GSON.toJson(response));
throw new NoXuiException();
}
String uhs = (String) response.displayClaims.xui.get(0).get("uhs");
if (existingUhs != null) {
if (!Objects.equals(uhs, existingUhs)) {
throw new ServerResponseMalformedException("uhs mismatched");
}
}
return uhs;
}
private MicrosoftSession authenticateViaLiveAccessToken(String liveAccessToken, String liveRefreshToken) throws IOException, JsonParseException, AuthenticationException {
String uhs;
XBoxLiveAuthenticationResponse xboxResponse, minecraftXstsResponse;
try {
// Authenticate with XBox Live
xboxResponse = HttpRequest
.POST("https://user.auth.xboxlive.com/user/authenticate")
.json(mapOf(
pair("Properties",
mapOf(pair("AuthMethod", "RPS"), pair("SiteName", "user.auth.xboxlive.com"),
pair("RpsTicket", "d=" + liveAccessToken))),
pair("RelyingParty", "http://auth.xboxlive.com"), pair("TokenType", "JWT")))
.retry(5)View on GitHub (pinned to 24702dc5a0)