HMCL-dev/HMCL · error · InvalidSkinException

Failed to read skin image

Error message

Failed to read skin image

What it means

Thrown by AccountListItem.uploadSkin when the locally selected skin file cannot be opened/read via Files.newInputStream — an IOException while reading the user-chosen skin file before upload. Wrapped in InvalidSkinException so the upload task fails with a user-facing message.

Solutions

  1. Confirm the skin file still exists and is readable, then re-select it in the account skin dialog
  2. Close any application holding a lock on the file and retry
  3. Copy the skin to a local writable path and upload from there
  4. Catch InvalidSkinException in the upload task and show the message to the user

Example fix

// before
accountListItem.uploadSkin(selectedFile);
// after
try {
    accountListItem.uploadSkin(selectedFile);
} catch (InvalidSkinException e) {
    LOG.warning("Skin upload failed: " + e.getMessage(), e);
    Dialogs.error("Could not read the skin file: " + selectedFile);
}
Defensive patterns

Strategy: validation

Validate before calling

if (!Files.isReadable(selectedFile)) throw new IllegalArgumentException("Skin file not readable: " + selectedFile);
if (Files.size(selectedFile) == 0) throw new IllegalArgumentException("Skin file is empty");

Try / catch

try {
    accountListItem.uploadSkin(file);
} catch (InvalidSkinException e) {
    Dialogs.error("Skin upload failed: " + e.getMessage());
}

Prevention

When it happens

Trigger: The selected skin file was moved/deleted between selection and upload, the file is locked by another process, or permission/network storage errors occur while opening the FileInputStream.

Common situations: User deletes or renames the skin PNG after picking it in the file chooser; file on a removable drive/UNC path that became unavailable; OS-level file lock from another application.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItem.java:164

        if (!account.canUploadSkin()) {
            return null;
        }

        FileChooser chooser = new FileChooser();
        chooser.setTitle(i18n("account.skin.upload"));
        chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("account.skin.file"), "*.png"));
        Path selectedFile = Controllers.showOpenDialog(chooser);
        if (selectedFile == null) {
            return null;
        }

        return refreshAsync()
                .thenRunAsync(() -> {
                    Image skinImg;
                    try (var input = Files.newInputStream(selectedFile)) {
                        skinImg = new Image(input);
                    } catch (IOException e) {
                        throw new InvalidSkinException("Failed to read skin image", e);
                    }
                    if (skinImg.isError()) {
                        throw new InvalidSkinException("Failed to read skin image", skinImg.getException());
                    }
                    if (skinImg.getWidth() != 64 || (skinImg.getHeight() != 32 && skinImg.getHeight() != 64)) {
                        throw new InvalidSkinException("Invalid skin size");
                    }
                    NormalizedSkin skin = new NormalizedSkin(skinImg);
                    String model = skin.isSlim() ? "slim" : "";
                    LOG.info("Uploading skin [" + selectedFile + "], model [" + model + "]");
                    account.uploadSkin(skin.isSlim(), selectedFile);
                })
                .thenComposeAsync(refreshAsync())
                .whenComplete(Schedulers.javafx(), e -> {
                    if (e != null) {
                        Controllers.dialog(Accounts.localizeErrorMessage(e), i18n("account.skin.upload.failed"), MessageType.ERROR);
                    }
                });

View on GitHub (pinned to 24702dc5a0)