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
- Ensure the name refers to a regular file at query time: check File.isFile() before calling
- Remove directories masquerading as file entries in the decoded tree, or fix the path/name mapping
- Avoid concurrent modification of the directory during build
- 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
- Filter directory entries out of file listings before size queries
- Avoid mutating the decoded tree while building
- Match names exactly (case) as produced by load()
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
- file must be a directory:
- Input file (" + path + ") was not found or was not readable.
- Framework path is not a directory:
- Framework path's parent is not a directory:
- Could not create framework directory:
AI-assisted analysis of iBotPeaches/Apktool@79b63384d7 (2026-08-14).
Data as JSON: /api/errors/afaef3a1fa521b54.
Report an issue: GitHub.