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
- Read the response body in the exception message — it contains Mojang's reason (e.g. invalid image, too large)
- Convert the skin to a valid PNG at most 64x64 (or 128x128 HD) and retry
- Match the variant parameter to the actual skin model (slim/alex vs classic/steve)
- 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
- Use valid 64x64 (or 128x128) PNG skins only
- Pass the correct slim/classic variant for the skin
- Refresh the access token if uploads return 401/403
- Read the server error body included in the exception before retrying
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
- https://api.minecraftservices.com/entitlements/mcstore
- https://api.minecraftservices.com/minecraft/profile
- Texture url is empty
- Failed to download theme background: HTTP
- Selected profile changed
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)