iBotPeaches/Apktool · error · DirectoryException

file must be a directory:

Error message

file must be a directory: 

What it means

FileDirectory's constructor requires an existing directory; it throws when the given File is not currently a directory (a regular file) or does not exist at all. This is a strict precondition — FileDirectory never creates the directory.

Source

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

import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;

public class FileDirectory extends Directory {
    private final File mDir;

    public FileDirectory(String dirName) throws DirectoryException {
        this(new File(dirName));
    }

    public FileDirectory(File dir) throws DirectoryException {
        if (!dir.isDirectory()) {
            throw new DirectoryException("file must be a directory: " + dir);
        }
        mDir = dir;
    }

    @Override
    protected void load() {
        mFiles = new LinkedHashSet<>();
        mDirs = new LinkedHashMap<>();

        File[] files = mDir.listFiles();
        Arrays.sort(files, Comparator.comparing(File::getName));

        for (File file : files) {
            if (file.isFile()) {
                mFiles.add(file.getName());
            } else {
                try {
                    mDirs.put(file.getName(), new FileDirectory(file));

View on GitHub (pinned to 79b63384d7)

Solutions

  1. Create the directory first: Files.createDirectories(path) or OS.mkdir(dir)
  2. Verify with isDirectory() before constructing and fail with a clear message
  3. Check for typos and symlinks resolving to files (ls -ld on the path)
  4. If another process may delete it, construct lazily right before use

Example fix

// before
Directory dir = new FileDirectory(new File("out/res")); // not created yet

// after
File f = new File("out/res");
if (!f.isDirectory()) {
    Files.createDirectories(f.toPath());
}
Directory dir = new FileDirectory(f);
Defensive patterns

Strategy: validation

Validate before calling

File f = new File(path);
if (!f.isDirectory()) {
    Files.createDirectories(f.toPath()); // throws if path is an existing FILE
}
Directory dir = new FileDirectory(f);

Type guard

boolean isUsableDirectory(File f) {
    return f.exists() && f.isDirectory() && f.canRead();
}

Try / catch

try {
    return new FileDirectory(dir);
} catch (DirectoryException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("file must be a directory")) {
        throw new IllegalArgumentException("Expected a directory but got: " + dir, e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Constructing FileDirectory with a path that is a file, a typo, or a not-yet-created directory; also reached via Directory.createDir chains when an intermediate step returned a non-directory.

Common situations: Assuming a decoded output folder exists before first decode; passing a path where a symlink points at a file; races where another process removes the directory between check and construction.

Related errors


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