HMCL-dev/HMCL · error · ServerResponseMalformedException

Failed to select character

Error message

Failed to select character

What it means

YggdrasilService.refresh throws ServerResponseMalformedException("Failed to select character") when a characterToSelect was requested but the refresh response's selectedProfile is null or has a different id. It means the server did not (or could not) bind the refreshed token to the requested character.

Solutions

  1. Re-authenticate (authenticate instead of refresh) and re-select a currently available character from availableProfiles.
  2. Pass null as characterToSelect if you don't require a specific character after refresh.
  3. Verify the character still exists on the auth server and re-add the account bound to a valid one.

Example fix

// before
service.refresh(accessToken, oldCharacter); // throws if character gone
// after
try {
    service.refresh(accessToken, oldCharacter);
} catch (ServerResponseMalformedException e) {
    YggdrasilSession fresh = service.authenticate(username, password);
    // reselect from fresh.getAvailableProfiles()
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (characterToSelect != null &&
    !availableProfileIds().contains(characterToSelect.getId())) {
    // character no longer exists server-side; skip selection
    characterToSelect = null;
}

Try / catch

try {
    service.refresh(accessToken, characterToSelect);
} catch (ServerResponseMalformedException e) {
    // re-authenticate and reselect from availableProfiles
    YggdrasilSession fresh = service.authenticate(username, password);
}

Prevention

When it happens

Trigger: Calling refresh(token, characterToSelect) where the character was deleted/renamed on the server, or the server ignores the selectedCharacter hint in its /refresh response.

Common situations: Switching characters on an authlib-injector server whose backend deleted the old one; account kept on another machine while the character was removed; server implementations not honoring the selectedCharacter field.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilService.java:116

    public YggdrasilSession refresh(String accessToken, String clientToken, GameProfile characterToSelect) throws AuthenticationException {
        Objects.requireNonNull(accessToken);
        Objects.requireNonNull(clientToken);

        Map<String, Object> request = createRequestWithCredentials(accessToken, clientToken);
        request.put("requestUser", true);

        if (characterToSelect != null) {
            request.put("selectedProfile", mapOf(
                    pair("id", UUIDs.toCompactString(characterToSelect.getId())),
                    pair("name", characterToSelect.getName())));
        }

        YggdrasilSession response = handleAuthenticationResponse(request(provider.getRefreshmentURL(), request), clientToken);

        if (characterToSelect != null) {
            if (response.getSelectedProfile() == null ||
                    !response.getSelectedProfile().getId().equals(characterToSelect.getId())) {
                throw new ServerResponseMalformedException("Failed to select character");
            }
        }

        return response;
    }

    public boolean validate(String accessToken) throws AuthenticationException {
        return validate(accessToken, null);
    }

    public boolean validate(String accessToken, String clientToken) throws AuthenticationException {
        Objects.requireNonNull(accessToken);

        try {
            requireEmpty(request(provider.getValidationURL(), createRequestWithCredentials(accessToken, clientToken)));
            return true;
        } catch (RemoteAuthenticationException e) {
            if ("ForbiddenOperationException".equals(e.getRemoteName())) {

View on GitHub (pinned to 24702dc5a0)