HMCL-dev/HMCL · error · IOException
Texture url is empty
Error message
Texture url is empty
What it means
loadTexture throws this IOException when a Minecraft skin/cape texture has a blank (null or whitespace-only) URL. The texture metadata exists but points to nothing, so there is no URL to download or hash from. It surfaces when HMCL tries to render the player skin.
Solutions
- Check StringUtils.isBlank(texture.url()) before calling loadTexture and fall back to a default/steve skin
- Re-login to refresh the profile so the texture metadata includes a URL
- If using a custom auth server, fix it to return the texture url field
- Wrap the call in try-catch and render a fallback texture instead of failing
Example fix
// before
LoadedTexture tex = TexturesLoader.loadTexture(texture);
// after
if (StringUtils.isBlank(texture.url())) {
texture = DEFAULT_STEVE_TEXTURE; // or skip skin rendering
}
LoadedTexture tex = TexturesLoader.loadTexture(texture); Defensive patterns
Strategy: fallback
Validate before calling
if (texture == null || StringUtils.isBlank(texture.url())) {
texture = DEFAULT_SKIN_TEXTURE;
} Type guard
boolean hasTextureUrl(Texture t) {
return t != null && !StringUtils.isBlank(t.url());
} Try / catch
try {
LoadedTexture tex = TexturesLoader.loadTexture(texture);
} catch (IOException e) {
LOG.warning("No texture URL, falling back to default skin", e);
return DEFAULT_LOADED_TEXTURE;
} Prevention
- Validate texture metadata (url non-blank) after login before rendering
- Refresh sessions when profile data looks stale
- Render a default skin for offline accounts proactively
When it happens
Trigger: Calling TexturesLoader.loadTexture(texture) with a Texture whose url() is blank; commonly reached through skinBinding when authentication returned a profile whose textures entry has no url property.
Common situations: Offline/cracked accounts with no uploaded skin; legacy profiles missing the texture URL; Mojang/Yggdrasil servers returning texture metadata without a URL; custom auth servers with incomplete texture responses.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Failed to download texture
- name existing
- Platform is mismatch: expected
- Failed to find current HMCL location
- Failed to get Java info from
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/a920c1b02565b469.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/game/TexturesLoader.java:95
private static final ThreadPoolExecutor POOL = threadPool("TexturesDownload", true, 2, 10, TimeUnit.SECONDS);
private static final Path TEXTURES_DIR = Metadata.HMCL_USER_HOME.resolve("skins");
private static Path getTexturePath(Texture texture) {
String url = texture.url();
int slash = url.lastIndexOf('/');
int dot = url.lastIndexOf('.');
if (dot < slash) {
dot = url.length();
}
String hash = url.substring(slash + 1, dot);
String prefix = hash.length() > 2 ? hash.substring(0, 2) : "xx";
return TEXTURES_DIR.resolve(prefix).resolve(hash);
}
public static LoadedTexture loadTexture(Texture texture) throws Throwable {
if (StringUtils.isBlank(texture.url())) {
throw new IOException("Texture url is empty");
}
Path file = getTexturePath(texture);
if (!Files.isRegularFile(file)) {
// download it
try {
new FileDownloadTask(texture.url(), file).run();
LOG.info("Texture downloaded: " + texture.url());
} catch (Exception e) {
if (Files.isRegularFile(file)) {
// concurrency conflict?
LOG.warning("Failed to download texture " + texture.url() + ", but the file is available", e);
} else {
throw new IOException("Failed to download texture " + texture.url());
}
}
}
View on GitHub (pinned to 24702dc5a0)