{"record":{"id":"afaef3a1fa521b54","repo":"iBotPeaches/Apktool","slug":"file-must-be-a-file","errorCode":null,"errorMessage":"file must be a file: ","messagePattern":"file must be a file: ","errorType":"exception","errorClass":"DirectoryException","httpStatus":null,"severity":"error","filePath":"brut.j.dir/src/main/java/brut/directory/FileDirectory.java","lineNumber":106,"sourceCode":"\n    @Override\n    protected void removeFileImpl(String name) {\n        File file = new File(generatePath(name));\n        OS.rmfile(file);\n    }\n\n    @Override\n    protected Directory createDirImpl(String name) throws DirectoryException {\n        File dir = new File(generatePath(name));\n        OS.mkdir(dir);\n        return new FileDirectory(dir);\n    }\n\n    @Override\n    public long getSize(String name) throws DirectoryException {\n        File file = new File(generatePath(name));\n        if (!file.isFile()) {\n            throw new DirectoryException(\"file must be a file: \" + file);\n        }\n        return file.length();\n    }\n\n    @Override\n    public long getCompressedSize(String name) throws DirectoryException {\n        return getSize(name);\n    }\n\n    @Override\n    public int getCompressionLevel(String name) {\n        return 0;\n    }\n}\n","sourceCodeStart":88,"sourceCodeEnd":121,"githubUrl":"https://github.com/iBotPeaches/Apktool/blob/79b63384d7d7e22917e6ea8b453272da7012515b/brut.j.dir/src/main/java/brut/directory/FileDirectory.java#L88-L121","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlong size = dir.getSize(\"res/layout\"); // actually a directory\n\n// after\nFile f = new File(dirDir, \"res/layout\");\nlong size = f.isFile() ? dir.getSize(\"res/layout\") : 0L; // or skip dirs explicitly","handlingStrategy":"validation","validationCode":"File f = new File(baseDir, name);\nif (!f.isFile()) {\n    // skip directories and stale names instead of calling getSize\n    return 0L;\n}\nreturn dir.getSize(name);","typeGuard":"boolean isRegularFileIn(File base, String name) {\n    File f = new File(base, name);\n    return f.isFile(); // false for dirs and missing paths\n}","tryCatchPattern":"try {\n    long size = dir.getSize(name);\n} catch (DirectoryException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"file must be a file\")) {\n        // name maps to a directory or vanished file: skip it, do not abort the whole build\n    } else { throw e; }\n}","preventionTips":["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()"],"tags":["apktool","directory","filesystem","validation"],"backgroundTag":null,"analyzedSha":"79b63384d7d7e22917e6ea8b453272da7012515b","analyzedAt":"2026-08-14T10:43:28.812Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}