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

  1. Check StringUtils.isBlank(texture.url()) before calling loadTexture and fall back to a default/steve skin
  2. Re-login to refresh the profile so the texture metadata includes a URL
  3. If using a custom auth server, fix it to return the texture url field
  4. 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

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


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)