iBotPeaches/Apktool · warning · InvalidPathException

Path is null or empty

Error message

Path is null or empty

What it means

BrutIO.sanitizePath rejects a path argument that is null or empty before doing any traversal checks. It is the first validation in apktool's zip-slip defense: paths coming from archive entries must be non-empty relative paths. InvalidPathException is thrown carrying the offending path and this reason.

Source

Thrown at brut.j.util/src/main/java/brut/util/BrutIO.java:103

                }
            }
        }
        return modified;
    }

    public static CRC32 calculateCrc(InputStream in) throws IOException {
        CRC32 crc = new CRC32();
        int bytesRead;
        byte[] buffer = new byte[8192];
        while ((bytesRead = in.read(buffer)) != -1) {
            crc.update(buffer, 0, bytesRead);
        }
        return crc;
    }

    public static String sanitizePath(File baseDir, String path) throws InvalidPathException, IOException {
        if (path == null || path.isEmpty()) {
            throw new InvalidPathException(path, "Path is null or empty");
        }

        Path origPath = Paths.get(path);
        if (origPath.isAbsolute()) {
            throw new InvalidPathException(path, "Absolute paths are not allowed");
        }

        Path basePath = Paths.get(baseDir.getCanonicalPath());
        Path resolvedPath = basePath.resolve(origPath).normalize();
        if (!resolvedPath.startsWith(basePath)) {
            throw new InvalidPathException(path, "Path traverses outside the base directory");
        }

        return basePath.relativize(resolvedPath).toString();
    }
}

View on GitHub (pinned to 79b63384d7)

Solutions

  1. Filter entries before sanitizing: skip null/empty names (often pure directory markers)
  2. Fix the producer side if you control archive creation so entries always have names
  3. Guard call sites that may legitimately have no path (optional attributes) instead of passing null
  4. Keep the other sanitizePath rules in mind too: absolute paths and traversal outside baseDir are rejected next

Example fix

// before
for (ZipEntry e : zipEntries) {
    Path p = Path.of(BrutIO.sanitizePath(baseDir, e.getName())); // empty dir-entry name throws
}

// after
for (ZipEntry e : zipEntries) {
    String name = e.getName();
    if (name == null || name.isEmpty()) {
        continue; // directory marker or malformed entry; nothing to extract
    }
    Path p = Path.of(BrutIO.sanitizePath(baseDir, name));
}
Defensive patterns

Strategy: validation

Validate before calling

String name = entry.getName();
if (name == null || name.isEmpty()) {
    continue; // skip empty/dir-marker entries before sanitizing
}
Path safe = Path.of(BrutIO.sanitizePath(baseDir, name));

Type guard

boolean isSanitizablePath(String path) {
    return path != null && !path.isEmpty();
}

Try / catch

try {
    Path p = Path.of(BrutIO.sanitizePath(baseDir, name));
} catch (InvalidPathException e) {
    if ("Path is null or empty".equals(e.getReason())) {
        // skip the entry (usually a directory marker) rather than aborting extraction
    } else {
        // other rejections: absolute path or traversal — log and quarantine the archive
    }
}

Prevention

When it happens

Trigger: Calling sanitizePath(baseDir, null) or sanitizePath(baseDir, ""), typically because an archive contained an entry whose name was empty, or because calling code passed an unvalidated/absent name field.

Common situations: Repacking archives that contain directory-only entries represented by empty names; programmatically iterating entries and passing a missing attribute; malformed zips produced by third-party packers.

Related errors


AI-assisted analysis of iBotPeaches/Apktool@79b63384d7 (2026-08-14). Data as JSON: /api/errors/6abd26d473b320b0. Report an issue: GitHub.