{"record":{"id":"6ba0ed19f6f274df","repo":"HMCL-dev/HMCL","slug":"destination-destfile-exists-but-is-read-only","errorCode":null,"errorMessage":"Destination '<destFile>' exists but is read-only","messagePattern":"Destination '<destFile>' exists but is read-only","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java","lineNumber":481,"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":463,"sourceCodeEnd":499,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java#L463-L499","documentation":"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.","triggerScenarios":"Calling FileUtils.copyFile when destFile already exists and its permission bits (or file locks/ownership on Windows) prevent writing.","commonSituations":"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.","solutions":["Close the process holding/using the destination file (e.g. running Minecraft)","Clear the read-only flag or fix permissions (chmod +w / file Properties > uncheck Read-only)","Delete the read-only destination first, then copy","Run the launcher with sufficient privileges or copy to a user-writable location"],"exampleFix":"// before\nFileUtils.copyFile(src, dest);\n// after\nif (Files.exists(dest) && !Files.isWritable(dest)) dest.toFile().setWritable(true);\nFileUtils.copyFile(src, dest);","handlingStrategy":"try-catch","validationCode":"if (Files.exists(destFile) && !Files.isWritable(destFile))\n    throw new AccessDeniedException(destFile.toString());","typeGuard":null,"tryCatchPattern":"try {\n    FileUtils.copyFile(src, dest);\n} catch (IOException e) {\n    if (e.getMessage().contains(\"read-only\")) {\n        // prompt user to close the game / fix permissions, then retry\n    } else throw e;\n}","preventionTips":["Ensure the game/launcher is not running when updating files it holds","Check Files.isWritable on destinations before overwriting","Avoid installing into admin-protected directories (Program Files, /usr)","Handle Windows read-only attributes after crashes or installers"],"tags":["io","file","permissions"],"backgroundTag":"file-write-permission-denied","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"}