HMCL-dev/HMCL · error · AuthenticationException

Failed to upload skin, response code:

Error message

Failed to upload skin, response code: 

What it means

uploadSkin POSTs the skin to the minecraftservices API; when the server answers with a non-2xx code, or returns a JSON body containing an errorMessage, HMCL throws AuthenticationException('Failed to upload skin, response code: <code>, response: <body>'). The server's own error payload is surfaced to explain why the upload was rejected.

Solutions

  1. Read the response body in the exception message — it contains Mojang's reason (e.g. invalid image, too large)
  2. Convert the skin to a valid PNG at most 64x64 (or 128x128 HD) and retry
  3. Match the variant parameter to the actual skin model (slim/alex vs classic/steve)
  4. Re-login to obtain a fresh access token if the code indicates 401/403, then retry the upload

Example fix

// before: uploading without validating the image
byte[] data = Files.readAllBytes(Paths.get(skinFile));
account.uploadSkin(false, data);
// after: pre-check format and size
BufferedImage img = ImageIO.read(new File(skinFile));
if (img == null || img.getWidth() > 128 || img.getHeight() > 128) {
    throw new IllegalArgumentException("Skin must be a valid PNG, 64x64 or 128x128");
}
account.uploadSkin(false, Files.readAllBytes(Paths.get(skinFile)));
Defensive patterns

Strategy: validation

Validate before calling

// validate skin payload before upload
BufferedImage img = ImageIO.read(new File(skinPath));
if (img == null) throw new IllegalArgumentException("Skin is not a readable PNG");
if (img.getWidth() > 128 || img.getHeight() > 128)
    throw new IllegalArgumentException("Skin dimensions exceed 128x128");

Try / catch

try {
    account.uploadSkin(slim, skinBytes);
} catch (AuthenticationException e) {
    if (e.getMessage().startsWith("Failed to upload skin")) {
        // message includes response code and Mojang's error body — surface to user
        logger.warning(e.getMessage());
    } else throw e;
}

Prevention

When it happens

Trigger: Uploading a skin with an invalid/oversized image, an unsupported skin variant, wrong content type for the multipart request, or an invalid/expired access token; the server returns 4xx/5xx or an error JSON body.

Common situations: Skin file is not a valid PNG or exceeds size limits; wrong 'variant' (slim vs classic) parameter; access token expired mid-session; Mojang rejecting uploads due to service issues; skin URL download failed producing a corrupt payload.

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/ac2373626cf6d799. Report an issue: GitHub.

Appendix: source

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

            HttpURLConnection con = NetworkUtils.createHttpConnection("https://api.minecraftservices.com/minecraft/profile/skins");
            con.setRequestMethod("POST");
            con.setRequestProperty("Authorization", "Bearer " + accessToken);
            con.setDoOutput(true);
            try (HttpMultipartRequest request = new HttpMultipartRequest(con)) {
                request.param("variant", isSlim ? "slim" : "classic");
                try (InputStream fis = Files.newInputStream(file)) {
                    request.file("file", FileUtils.getName(file), "image/" + FileUtils.getExtension(file), fis);
                }
            }

            String response = NetworkUtils.readFullyAsString(con);
            if (StringUtils.isBlank(response)) {
                if (con.getResponseCode() / 100 != 2)
                    throw new ResponseCodeException(con.getURL().toURI(), con.getResponseCode());
            } else {
                MinecraftErrorResponse profileResponse = GSON.fromJson(response, MinecraftErrorResponse.class);
                if (StringUtils.isNotBlank(profileResponse.errorMessage) || con.getResponseCode() / 100 != 2)
                    throw new AuthenticationException("Failed to upload skin, response code: " + con.getResponseCode() + ", response: " + response);
            }
        } catch (IOException | JsonParseException | URISyntaxException e) {
            throw new AuthenticationException(e);
        }
    }

    private static String request(String url, Object payload) throws AuthenticationException {
        try {
            if (payload == null)
                return NetworkUtils.doGet(url);
            else
                return NetworkUtils.doPost(NetworkUtils.toURI(url), payload instanceof String ? (String) payload : GSON.toJson(payload), "application/json");
        } catch (IOException e) {
            throw new ServerDisconnectException(e);
        }
    }

    public static class XboxAuthorizationException extends AuthenticationException {

View on GitHub (pinned to 24702dc5a0)