HMCL-dev/HMCL · error

Destination ' ' exists but is read-only

Error message

Destination '<destFile>' exists but is read-only

What it means

Guard in FileUtils.copyFile: the destination file already exists but is not writable, so the REPLACE_EXISTING copy would fail; the method rejects it up front with an IOException naming destFile. Generic pre-condition validation of the destination argument.

Solutions

  1. Close the process holding/using the destination file (e.g. running Minecraft)
  2. Clear the read-only flag or fix permissions (chmod +w / file Properties > uncheck Read-only)
  3. Delete the read-only destination first, then copy
  4. Run the launcher with sufficient privileges or copy to a user-writable location

Example fix

// before
FileUtils.copyFile(src, dest);
// after
if (Files.exists(dest) && !Files.isWritable(dest)) dest.toFile().setWritable(true);
FileUtils.copyFile(src, dest);
Defensive patterns

Strategy: try-catch

Validate before calling

if (Files.exists(destFile) && !Files.isWritable(destFile))
    throw new AccessDeniedException(destFile.toString());

Try / catch

try {
    FileUtils.copyFile(src, dest);
} catch (IOException e) {
    if (e.getMessage().contains("read-only")) {
        // prompt user to close the game / fix permissions, then retry
    } else throw e;
}

Prevention

When it happens

Trigger: Calling FileUtils.copyFile when destFile already exists and its permission bits (or file locks/ownership on Windows) prevent writing.

Common situations: Overwriting game files locked by a running instance of the game/launcher; files left read-only by a previous crash or installer; copying into system-protected directories without admin rights.

Understand the failure class

Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.

Related errors


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

Appendix: source

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

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)