HMCL-dev/HMCL · error

Source ' ' exists but is a directory

Error message

Source '<srcFile>' exists but is a directory

What it means

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.

Solutions

  1. Pass the full path to the individual file, not its containing directory
  2. Check Files.isDirectory(srcFile) beforehand and use a recursive copy utility for directories
  3. Correct the configuration/variable that holds the wrong path

Example fix

// before
FileUtils.copyFile(modsDir, backupDir.resolve("mods"));
// after
for (Path f : FileUtils.listFilesByExtension(modsDir, ".jar"))
    FileUtils.copyFile(f, backupDir.resolve(modsDir.relativize(f)));
Defensive patterns

Strategy: validation

Validate before calling

if (Files.isDirectory(srcFile))
    throw new IllegalArgumentException("expected a file, got directory: " + srcFile);

Type guard

static boolean isRegularFileOrNull(Path p) { return p != null && Files.isRegularFile(p) && !Files.isDirectory(p); }

Try / catch

try {
    FileUtils.copyFile(src, dest);
} catch (IOException e) {
    if (e.getMessage().contains("is a directory")) {
        // switch to a recursive directory copy
    } else throw e;
}

Prevention

When it happens

Trigger: Passing a directory path (e.g. the .minecraft folder itself, or a folder expected to contain the file) as srcFile to FileUtils.copyFile.

Common situations: 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.

Related errors


AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10). Data as JSON: /api/errors/f4e774e07a90e22a. Report an issue: GitHub.

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java:478

public static void copyFile(Path srcFile, Path destFile)
        throws IOException {
    Objects.requireNonNull(srcFile, "Source must not be null");
    Objects.requireNonNull(destFile, "Destination must not be null");
    if (!Files.exists(srcFile))
        throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
    if (Files.isDirectory(srcFile))
        throw new IOException("Source '" + srcFile + "' exists but is a directory");
    Files.createDirectories(destFile.getParent());
    if (Files.exists(destFile) && !Files.isWritable(destFile))
        throw new IOException("Destination '" + destFile + "' exists but is read-only");

    Files.copy(srcFile, destFile, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
}

View on GitHub (pinned to 24702dc5a0)