{"record":{"id":"39e3c2d025d8903c","repo":"HMCL-dev/HMCL","slug":"uhs-mismatched","errorCode":null,"errorMessage":"uhs mismatched","messagePattern":"uhs mismatched","errorType":"exception","errorClass":"ServerResponseMalformedException","httpStatus":null,"severity":"error","filePath":"HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java","lineNumber":112,"sourceCode":"        } catch (JsonParseException e) {\n            throw new ServerResponseMalformedException(e);\n        }\n    }\n\n    private String getUhs(XBoxLiveAuthenticationResponse response, String existingUhs) throws AuthenticationException {\n        if (response.errorCode != 0) {\n            throw new XboxAuthorizationException(response.errorCode, response.redirectUrl);\n        }\n\n        if (response.displayClaims == null || response.displayClaims.xui == null || response.displayClaims.xui.size() == 0 || !response.displayClaims.xui.get(0).containsKey(\"uhs\")) {\n            LOG.warning(\"Unrecognized xbox authorization response \" + GSON.toJson(response));\n            throw new NoXuiException();\n        }\n\n        String uhs = (String) response.displayClaims.xui.get(0).get(\"uhs\");\n        if (existingUhs != null) {\n            if (!Objects.equals(uhs, existingUhs)) {\n                throw new ServerResponseMalformedException(\"uhs mismatched\");\n            }\n        }\n        return uhs;\n    }\n\n    private MicrosoftSession authenticateViaLiveAccessToken(String liveAccessToken, String liveRefreshToken) throws IOException, JsonParseException, AuthenticationException {\n        String uhs;\n        XBoxLiveAuthenticationResponse xboxResponse, minecraftXstsResponse;\n        try {\n            // Authenticate with XBox Live\n            xboxResponse = HttpRequest\n                    .POST(\"https://user.auth.xboxlive.com/user/authenticate\")\n                    .json(mapOf(\n                            pair(\"Properties\",\n                                    mapOf(pair(\"AuthMethod\", \"RPS\"), pair(\"SiteName\", \"user.auth.xboxlive.com\"),\n                                            pair(\"RpsTicket\", \"d=\" + liveAccessToken))),\n                            pair(\"RelyingParty\", \"http://auth.xboxlive.com\"), pair(\"TokenType\", \"JWT\")))\n                    .retry(5)","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java#L94-L130","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: refreshing stale tokens across an account switch\naccount.logIn();\n// after: detect uhs mismatch and force clean re-login\ntry {\n    account.logIn();\n} catch (ServerResponseMalformedException e) {\n    if (e.getMessage().contains(\"uhs mismatched\")) {\n        accounts.removeAccount(account);\n        accounts.createAccount(Accounts.OAUTH_MICROSOFT).logIn();\n    } else {\n        throw e;\n    }\n}","handlingStrategy":"try-catch","validationCode":"// pre-compare stored uhs if you persist it\nString storedUhs = getStoredUhs();\nif (storedUhs != null && !storedUhs.equals(freshUhs)) {\n    scheduleCleanReauth(); // don't reuse old refresh token\n}","typeGuard":null,"tryCatchPattern":"try {\n    account.logIn();\n} catch (ServerResponseMalformedException e) {\n    if (\"uhs mismatched\".equals(e.getMessage())) {\n        accounts.removeAccount(account);\n        accounts.createAccount(Accounts.OAUTH_MICROSOFT).logIn();\n    } else throw e;\n}","preventionTips":["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"],"tags":["authentication","xbox","microsoft","session"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}