{"record":{"id":"691a41b24c8920bf","repo":"iBotPeaches/Apktool","slug":"file-must-be-a-directory","errorCode":null,"errorMessage":"file must be a directory: ","messagePattern":"file must be a directory: ","errorType":"exception","errorClass":"DirectoryException","httpStatus":null,"severity":"error","filePath":"brut.j.dir/src/main/java/brut/directory/FileDirectory.java","lineNumber":40,"sourceCode":"import java.io.InputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.nio.file.Files;\nimport java.util.Arrays;\nimport java.util.Comparator;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\n\npublic class FileDirectory extends Directory {\n    private final File mDir;\n\n    public FileDirectory(String dirName) throws DirectoryException {\n        this(new File(dirName));\n    }\n\n    public FileDirectory(File dir) throws DirectoryException {\n        if (!dir.isDirectory()) {\n            throw new DirectoryException(\"file must be a directory: \" + dir);\n        }\n        mDir = dir;\n    }\n\n    @Override\n    protected void load() {\n        mFiles = new LinkedHashSet<>();\n        mDirs = new LinkedHashMap<>();\n\n        File[] files = mDir.listFiles();\n        Arrays.sort(files, Comparator.comparing(File::getName));\n\n        for (File file : files) {\n            if (file.isFile()) {\n                mFiles.add(file.getName());\n            } else {\n                try {\n                    mDirs.put(file.getName(), new FileDirectory(file));","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/iBotPeaches/Apktool/blob/79b63384d7d7e22917e6ea8b453272da7012515b/brut.j.dir/src/main/java/brut/directory/FileDirectory.java#L22-L58","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Create the directory first: Files.createDirectories(path) or OS.mkdir(dir)","Verify with isDirectory() before constructing and fail with a clear message","Check for typos and symlinks resolving to files (ls -ld on the path)","If another process may delete it, construct lazily right before use"],"exampleFix":"// before\nDirectory dir = new FileDirectory(new File(\"out/res\")); // not created yet\n\n// after\nFile f = new File(\"out/res\");\nif (!f.isDirectory()) {\n    Files.createDirectories(f.toPath());\n}\nDirectory dir = new FileDirectory(f);","handlingStrategy":"validation","validationCode":"File f = new File(path);\nif (!f.isDirectory()) {\n    Files.createDirectories(f.toPath()); // throws if path is an existing FILE\n}\nDirectory dir = new FileDirectory(f);","typeGuard":"boolean isUsableDirectory(File f) {\n    return f.exists() && f.isDirectory() && f.canRead();\n}","tryCatchPattern":"try {\n    return new FileDirectory(dir);\n} catch (DirectoryException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"file must be a directory\")) {\n        throw new IllegalArgumentException(\"Expected a directory but got: \" + dir, e);\n    }\n    throw e;\n}","preventionTips":["Always Files.createDirectories before constructing FileDirectory","Never assume decode output folders pre-exist","Check for symlinks pointing at regular files on the intended path"],"tags":["apktool","directory","filesystem","validation"],"backgroundTag":null,"analyzedSha":"79b63384d7d7e22917e6ea8b453272da7012515b","analyzedAt":"2026-08-14T10:43:28.812Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}