iBotPeaches/Apktool · error · DirectoryException

file must be a file:

Error message

file must be a file: 

What it means

FileDirectory.getSize(name) stat's the requested path and requires it to be a regular file; it throws when the path is a directory or does not exist. It is the filesystem implementation of Directory size queries used when repacking.

Source

Thrown at brut.j.dir/src/main/java/brut/directory/FileDirectory.java:106

    @Override
    protected void removeFileImpl(String name) {
        File file = new File(generatePath(name));
        OS.rmfile(file);
    }

    @Override
    protected Directory createDirImpl(String name) throws DirectoryException {
        File dir = new File(generatePath(name));
        OS.mkdir(dir);
        return new FileDirectory(dir);
    }

    @Override
    public long getSize(String name) throws DirectoryException {
        File file = new File(generatePath(name));
        if (!file.isFile()) {
            throw new DirectoryException("file must be a file: " + file);
        }
        return file.length();
    }

    @Override
    public long getCompressedSize(String name) throws DirectoryException {
        return getSize(name);
    }

    @Override
    public int getCompressionLevel(String name) {
        return 0;
    }
}

View on GitHub (pinned to 79b63384d7)

Solutions

  1. Ensure the name refers to a regular file at query time: check File.isFile() before calling
  2. Remove directories masquerading as file entries in the decoded tree, or fix the path/name mapping
  3. Avoid concurrent modification of the directory during build
  4. On case-sensitive filesystems, verify entry names match exactly what load() listed

Example fix

// before
long size = dir.getSize("res/layout"); // actually a directory

// after
File f = new File(dirDir, "res/layout");
long size = f.isFile() ? dir.getSize("res/layout") : 0L; // or skip dirs explicitly
Defensive patterns

Strategy: validation

Validate before calling

File f = new File(baseDir, name);
if (!f.isFile()) {
    // skip directories and stale names instead of calling getSize
    return 0L;
}
return dir.getSize(name);

Type guard

boolean isRegularFileIn(File base, String name) {
    File f = new File(base, name);
    return f.isFile(); // false for dirs and missing paths
}

Try / catch

try {
    long size = dir.getSize(name);
} catch (DirectoryException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("file must be a file")) {
        // name maps to a directory or vanished file: skip it, do not abort the whole build
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling getSize on a name that maps to a subdirectory of the FileDirectory, or on a name that was deleted/never created between listing and sizing.

Common situations: Repacking a decoded project where a listed 'file' entry is actually an extracted folder; tools mutating the output tree while apktool builds; case-mismatched names on case-sensitive filesystems.

Related errors


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