HMCL-dev/HMCL · error · IOException

Theme-pack entry is empty

Error message

Theme-pack entry is empty

What it means

normalizeThemePackEntryName trims whitespace, converts backslashes to slashes, and strips one trailing slash from each zip entry name. If nothing remains after that normalization, the entry name is empty and this IOException is thrown. Empty entry names cannot map to a filesystem path.

Solutions

  1. Remove the empty-named entry from the zip (repack excluding it).
  2. Regenerate the archive with a reliable zip tool or ThemePackExporter.
  3. If generating entries programmatically, assert names are non-empty before writing them.

Example fix

// before
zip.putNextEntry(new ZipArchiveEntry("  "));
// after
if (!name.isBlank()) zip.putNextEntry(new ZipArchiveEntry(name.trim().replace('\\', '/')));
Defensive patterns

Strategy: validation

Validate before calling

for (var e : Collections.list(new ZipFile(pack).entries())) {
    if (e.getName().isBlank() || e.getName().equals("/")) throw new IllegalArgumentException("empty entry name in " + pack);
}

Try / catch

try {
    ThemePackManager.install(pack, dir);
} catch (IOException e) {
    if (e.getMessage().contains("entry is empty")) {
      ui.show("Archive is corrupted (blank entry name); re-download or re-export it.");
    } else throw e;
}

Prevention

When it happens

Trigger: Installing a theme-pack zip containing an entry whose name is empty, whitespace-only, or just '/'; archives created by buggy tools writing blank-name entries.

Common situations: Corrupted or programmatically generated zips with blank entry names; entries consisting only of '/' created by faulty packers.

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

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java:1403

    /// Moves a file into place, using an atomic move when the platform supports it.
    private static void moveReplacing(Path source, Path target) throws IOException {
        try {
            Files.move(source, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
        } catch (AtomicMoveNotSupportedException e) {
            Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
        }
    }

    /// Returns a normalized and safe theme-pack zip entry name.
    private static String normalizeThemePackEntryName(String entryName) throws IOException {
        Objects.requireNonNull(entryName);

        String normalized = entryName.trim().replace('\\', '/');
        if (normalized.endsWith("/")) {
            normalized = normalized.substring(0, normalized.length() - 1);
        }
        if (normalized.isEmpty()) {
            throw new IOException("Theme-pack entry is empty");
        }
        if (normalized.startsWith("/") || normalized.matches("^[A-Za-z]:.*")) {
            throw new IOException("Theme-pack entry must be relative: " + entryName);
        }

        for (String segment : normalized.split("/")) {
            if (segment.isEmpty() || ".".equals(segment) || "..".equals(segment)) {
                throw new IOException("Theme-pack entry contains an unsafe segment: " + entryName);
            }
            for (int i = 0; i < segment.length(); i++) {
                char ch = segment.charAt(i);
                if (Character.isISOControl(ch) || ch == '\0') {
                    throw new IOException("Theme-pack entry contains a control character: " + entryName);
                }
            }
        }
        return normalized;
    }

View on GitHub (pinned to 24702dc5a0)