{"record":{"id":"f4e774e07a90e22a","repo":"HMCL-dev/HMCL","slug":"source-srcfile-exists-but-is-a-directory","errorCode":null,"errorMessage":"Source '<srcFile>' exists but is a directory","messagePattern":"Source '<srcFile>' exists but is a directory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java","lineNumber":478,"sourceCode":"public static void copyFile(Path srcFile, Path destFile)\n        throws IOException {\n    Objects.requireNonNull(srcFile, \"Source must not be null\");\n    Objects.requireNonNull(destFile, \"Destination must not be null\");\n    if (!Files.exists(srcFile))\n        throw new FileNotFoundException(\"Source '\" + srcFile + \"' does not exist\");\n    if (Files.isDirectory(srcFile))\n        throw new IOException(\"Source '\" + srcFile + \"' exists but is a directory\");\n    Files.createDirectories(destFile.getParent());\n    if (Files.exists(destFile) && !Files.isWritable(destFile))\n        throw new IOException(\"Destination '\" + destFile + \"' exists but is read-only\");\n\n    Files.copy(srcFile, destFile, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);\n}","sourceCodeStart":460,"sourceCodeEnd":496,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java#L460-L496","documentation":"Guard in FileUtils.copyFile: the source path exists but is a directory. copyFile only copies regular files; callers must use a directory-copy routine instead. Generic pre-condition validation of the srcFile argument.","triggerScenarios":"Passing a directory path (e.g. the .minecraft folder itself, or a folder expected to contain the file) as srcFile to FileUtils.copyFile.","commonSituations":"Config errors pointing at a directory instead of a file (e.g. 'assets/' vs 'assets/index.json'); user-selected folders fed to a file-copy API; a path that changed from file to directory between versions.","solutions":["Pass the full path to the individual file, not its containing directory","Check Files.isDirectory(srcFile) beforehand and use a recursive copy utility for directories","Correct the configuration/variable that holds the wrong path"],"exampleFix":"// before\nFileUtils.copyFile(modsDir, backupDir.resolve(\"mods\"));\n// after\nfor (Path f : FileUtils.listFilesByExtension(modsDir, \".jar\"))\n    FileUtils.copyFile(f, backupDir.resolve(modsDir.relativize(f)));","handlingStrategy":"validation","validationCode":"if (Files.isDirectory(srcFile))\n    throw new IllegalArgumentException(\"expected a file, got directory: \" + srcFile);","typeGuard":"static boolean isRegularFileOrNull(Path p) { return p != null && Files.isRegularFile(p) && !Files.isDirectory(p); }","tryCatchPattern":"try {\n    FileUtils.copyFile(src, dest);\n} catch (IOException e) {\n    if (e.getMessage().contains(\"is a directory\")) {\n        // switch to a recursive directory copy\n    } else throw e;\n}","preventionTips":["Point copy operations at concrete files, never directories","Validate user-selected paths (file vs directory choosers)","Use a recursive copy helper when directory semantics are needed"],"tags":["io","file","copy"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}