HMCL-dev/HMCL · critical · java.io.IOException

Unable to move temp file from

Error message

Unable to move temp file from <temp> to <file>

What it means

FileDownloadTask's close() finalizes a download by atomically moving the temp file to its destination via Files.move(...REPLACE_EXISTING). This IOException wraps any failure during that move, preserving the original cause, since the downloaded data cannot be put in place.

Solutions

  1. Close any process locking the destination file (check antivirus, running game, file managers) and retry
  2. Verify write permission on the destination directory and free disk space
  3. Ensure the temp file and destination are on the same filesystem, or perform a copy+delete fallback
  4. Catch the IOException and inspect the wrapped cause for the exact OS error

Example fix

// before
downloadTask.run(); // IOException with wrapped move failure
// after
try {
    downloadTask.run();
} catch (IOException e) {
    LOG.warning("Download OK but install failed: " + e.getCause());
    Files.deleteIfExists(tempPath);
}
Defensive patterns

Strategy: try-catch

Validate before calling

Path target = file.toAbsolutePath();
Files.createDirectories(target.getParent());
if (!Files.isWritable(target.getParent()))
    throw new AccessDeniedException("Destination not writable: " + target.getParent());

Try / catch

try {
    downloadTask.run();
} catch (IOException e) {
    if (e.getMessage().startsWith("Unable to move temp file")) {
        LOG.warning("Install failed: " + e.getCause());
        promptUserToCloseLockingProcesses();
    } else throw e;
}

Prevention

When it happens

Trigger: The move fails after a successful download — typically because the destination is locked/open by another process, the target directory permissions changed, or the filesystem is full/read-only (cross-device moves also fail without REPLACE semantics).

Common situations: Windows antivirus or another program holding the target .jar open; destination on a read-only or full disk; the file being executed while being replaced; permission changes on the game directory.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java:261

                try {
                    for (IntegrityCheckHandler handler : integrityCheckHandlers) {
                        handler.checkIntegrity(temp, file);
                    }

                    if (checksum != null && !checksum.isEmpty()) {
                        String actualChecksum = HexFormat.of().formatHex(digest.digest());
                        if (!checksum.equalsIgnoreCase(actualChecksum)) {
                            throw new ChecksumMismatchException(algorithm, checksum, actualChecksum);
                        }
                    }

                    Files.createDirectories(file.toAbsolutePath().getParent());

                    try {
                        Files.move(temp, file, StandardCopyOption.REPLACE_EXISTING);
                        moved = true;
                    } catch (Exception e) {
                        throw new IOException("Unable to move temp file from " + temp + " to " + file, e);
                    }

                    if (caching && algorithm != null) {
                        try {
                            repository.cacheFile(file, algorithm, checksum);
                        } catch (IOException e) {
                            LOG.warning("Failed to cache file", e);
                        }
                    }

                    if (checkETag) {
                        repository.cacheRemoteFile(response, file);
                    }
                } finally {
                    if (!moved) {
                        deleteTempFile();
                    }
                }

View on GitHub (pinned to 24702dc5a0)