beemdevelopment/Aegis · error · IOException

Unable to find relative to the root of the ZIP file

Error message

Unable to find %s relative to the root of the ZIP file

What it means

After passing the path-safety check, importPack looks up each icon's relative filename as a ZIP entry at the archive root. If the entry is missing (zipFile.getFileHeader returns null), the pack definition references a file that isn't in the archive, so an IOException naming the missing relative path is thrown and wrapped as IconPackException.

Solutions

  1. Rebuild the ZIP so every icon file named in pack.json exists at the archive root
  2. Compare pack.json's icon list against the actual ZIP entries (unzip -l) and fix mismatches
  3. Re-download the icon pack from its official source
  4. If you authored the pack, add the missing icon files before redistributing

Example fix

// before (shell)
zip pack.zip pack.json            # forgot the icon files
// after (shell)
cd aegis-icons && zip ../pack.zip pack.json *.png
Defensive patterns

Strategy: validation

Validate before calling

java.util.zip.ZipFile zf = new java.util.zip.ZipFile(inFile);
for (IconPack.Icon icon : pack.getIcons()) {
    if (zf.getEntry(icon.getRelativeFilename()) == null) {
        throw new IconPackException("Missing ZIP entry: " + icon.getRelativeFilename());
    }
}
zf.close();

Try / catch

try {
    iconPackManager.importPack(file);
} catch (IconPackException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Unable to find")) {
        UiHelper.showDialog(context, R.string.incomplete_icon_pack);
    }
}

Prevention

When it happens

Trigger: Importing an icon pack where pack.json lists icons whose files were not added to the ZIP, were renamed, or live in a subdirectory while the definition expects them at the root.

Common situations: Pack author edits pack.json to add icons but forgets to add the image files before zipping; re-zipping introduces a nested folder so entries shift; icons deleted from the archive after the definition was written.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of beemdevelopment/Aegis@d6f4e5925a (2026-09-08). Data as JSON: /api/errors/a0e5f24ad3b17b2f. Report an issue: GitHub.

Appendix: source

Thrown at app/src/main/java/com/beemdevelopment/aegis/icons/IconPackManager.java:103

                throw new IconPackExistsException(pack);
            }
            IconPack existingPack = getIconPackByUUID(pack.getUUID());
            if (existingPack != null) {
                throw new IconPackExistsException(existingPack);
            }
            if (!packDir.exists() && !packDir.mkdirs()) {
                throw new IOException(String.format("Unable to create directories: %s", packDir.toString()));
            }

            // extract each of the defined icons to the icon pack directory
            for (IconPack.Icon icon : pack.getIcons()) {
                File destFile = new File(packDir, icon.getRelativeFilename());
                if (!destFile.getCanonicalPath().startsWith(packDir.getCanonicalPath() + File.separator)) {
                    throw new IOException("Attempted to write outside of the icon pack directory");
                }
                FileHeader iconHeader = zipFile.getFileHeader(icon.getRelativeFilename());
                if (iconHeader == null) {
                    throw new IOException(String.format("Unable to find %s relative to the root of the ZIP file", icon.getRelativeFilename()));
                }

                // create new directories for this file if needed
                File parent = destFile.getParentFile();
                if (parent != null && !parent.exists() && !parent.mkdirs()) {
                    throw new IOException(String.format("Unable to create directories: %s", packDir.toString()));
                }

                try (ZipInputStream inStream = zipFile.getInputStream(iconHeader);
                     FileOutputStream outStream = new FileOutputStream(destFile)) {
                    IOUtils.copy(inStream, outStream);
                }

                // after successful copy of the icon, store the new filename
                icon.setFile(destFile);
            }

            // write the icon pack definition file to the newly created directory

View on GitHub (pinned to d6f4e5925a)