HMCL-dev/HMCL · error · ResponseCodeException

https://api.minecraftservices.com/minecraft/profile

Error message

https://api.minecraftservices.com/minecraft/profile

What it means

getMinecraftProfile requests GET https://api.minecraftservices.com/minecraft/profile to fetch the player's profile. If the response code is neither 204 (handled specially, raising license/profile-not-found exceptions) nor 200, ResponseCodeException with the endpoint URL is thrown, meaning an unexpected HTTP status blocked profile retrieval.

Solutions

  1. Wait and retry — 429/5xx are usually transient; space out repeated logins/validations
  2. Re-authenticate the account so a fresh access token is used (401 usually means token expired)
  3. Check Mojang/Xbox service status for ongoing incidents
  4. Check local network/proxy configuration that might tamper with HTTPS responses

Example fix

// before: validating aggressively in a loop
while (!account.getService().validate(...)) { }
// after: back off and refresh on unexpected statuses
if (!account.getService().validate(notAfter, tokenType, accessToken)) {
    Thread.sleep(30_000); // avoid 429 rate limit on /minecraft/profile
    account.logIn(); // get a fresh access token
}
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight the profile endpoint before heavy operations
HttpURLConnection c = HttpRequest.GET("https://api.minecraftservices.com/minecraft/profile")
        .authorization("Bearer " + accessToken).createConnection();
int code = c.getResponseCode();
if (code == 429) Thread.sleep(60_000); // rate limited
else if (code == 401) refreshTokenFirst();

Try / catch

try {
    account.validate(notAfter, tokenType, accessToken);
} catch (ResponseCodeException e) {
    if (String.valueOf(e.getMessage()).contains("/minecraft/profile")) {
        Thread.sleep(30_000); // back off, then refresh credentials and retry
        account.logIn();
    } else throw e;
}

Prevention

When it happens

Trigger: Called from profileResponse and validate; the endpoint returns 401 (access token expired or invalid), 403, 429 (rate limited), or 5xx — anything unexpected — so validate() or profile fetching fails with this exception.

Common situations: Mojang rate limiting after many logins (429); token expiry between login steps; temporary microsoftservices outage (5xx); overly frequent validate calls; network middleboxes altering the response.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java:264

    private static MinecraftProfileResponse getMinecraftProfile(String tokenType, String accessToken)
            throws IOException, AuthenticationException {
        HttpURLConnection conn = HttpRequest.GET("https://api.minecraftservices.com/minecraft/profile")
                .authorization(tokenType, accessToken)
                .createConnection();
        int responseCode = conn.getResponseCode();
        if (responseCode == HTTP_NOT_FOUND) {
            MinecraftLicense license = HttpRequest.GET("https://api.minecraftservices.com/entitlements/license")
                    .authorization(tokenType, accessToken)
                    .getJson(MinecraftLicense.class);
            boolean hasMinecraftLicense = license != null && license.items() != null && license.items().stream()
                    .anyMatch(item -> "game_minecraft".equals(item.name()));
            if (!hasMinecraftLicense) {
                throw new MinecraftJavaEditionLicenseNotFoundException();
            } else {
                throw new MinecraftJavaEditionProfileNotFoundException();
            }
        } else if (responseCode != 200) {
            throw new ResponseCodeException("https://api.minecraftservices.com/minecraft/profile", responseCode);
        }

        String result = NetworkUtils.readFullyAsString(conn);
        return JsonUtils.fromNonNullJson(result, MinecraftProfileResponse.class);
    }

    public Optional<CompleteGameProfile> getCompleteGameProfile(UUID uuid) throws AuthenticationException {
        Objects.requireNonNull(uuid);

        return Optional.ofNullable(GSON.fromJson(request("https://sessionserver.mojang.com/session/minecraft/profile/" + UUIDs.toCompactString(uuid), null), CompleteGameProfile.class));
    }

    public void uploadSkin(String accessToken, boolean isSlim, Path file) throws AuthenticationException, UnsupportedOperationException {
        try {
            HttpURLConnection con = NetworkUtils.createHttpConnection("https://api.minecraftservices.com/minecraft/profile/skins");
            con.setRequestMethod("POST");
            con.setRequestProperty("Authorization", "Bearer " + accessToken);
            con.setDoOutput(true);

View on GitHub (pinned to 24702dc5a0)