chinabugotech/hutool · error · IORuntimeException
Files '{}' and '{}' are equal
Error message
Files '{}' and '{}' are equal What it means
FileUtil.copyFile compares src and dest by canonical path via FileUtil.equals. If they resolve to the same file, copying would read and write the same inode and truncate the source, so it throws IORuntimeException("Files '{}' and '{}' are equal") to prevent data loss.
Source
Thrown at hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java:1141
/**
* 通过JDK7+的 Files#copy(Path, Path, CopyOption...) 方法拷贝文件
*
* @param src 源文件
* @param dest 目标文件或目录,如果为目录使用与源文件相同的文件名
* @param options {@link StandardCopyOption}
* @return 目标文件
* @throws IORuntimeException IO异常
*/
public static File copyFile(File src, File dest, StandardCopyOption... options) throws IORuntimeException {
// check
Assert.notNull(src, "Source File is null !");
if (false == src.exists()) {
throw new IORuntimeException("File not exist: " + src);
}
Assert.notNull(dest, "Destination File or directiory is null !");
if (equals(src, dest)) {
throw new IORuntimeException("Files '{}' and '{}' are equal", src, dest);
}
return copyFile(src.toPath(), dest.toPath(), options).toFile();
}
/**
* 复制文件或目录<br>
* 如果目标文件为目录,则将源文件以相同文件名拷贝到目标目录
*
* @param srcPath 源文件或目录
* @param destPath 目标文件或目录,目标不存在会自动创建(目录、文件都创建)
* @param isOverride 是否覆盖目标文件
* @return 目标目录或文件
* @throws IORuntimeException IO异常
*/
public static File copy(String srcPath, String destPath, boolean isOverride) throws IORuntimeException {
return copy(file(srcPath), file(destPath), isOverride);
}
View on GitHub (pinned to 8870454b2a)
Solutions
- Ensure dest is a genuinely different path from src.
- Generate a unique dest name (append a timestamp or counter).
- Check FileUtil.equals(src, dest) beforehand and skip or rename.
Example fix
// before
FileUtil.copyFile(file, file); // throws: equal
// after
File dest = new File(file.getParent(), "copy_" + file.getName());
if (!FileUtil.equals(file, dest)) {
FileUtil.copyFile(file, dest);
} Defensive patterns
Strategy: validation
Validate before calling
if (!FileUtil.equals(src, dest)) {
FileUtil.copyFile(src, dest, options);
} Type guard
static boolean areDistinctFiles(File a, File b) {
return a != null && b != null && !FileUtil.equals(a, b);
} Try / catch
try {
return FileUtil.copyFile(src, dest, options);
} catch (IORuntimeException e) {
if (e.getMessage().contains("are equal")) {
// src and dest are the same file: pick a different dest
}
throw e;
} Prevention
- Check FileUtil.equals(src, dest) before copying.
- Generate unique destination names for copies/backups.
- Be aware symlinks/hardlinks can make distinct paths resolve to one inode.
When it happens
Trigger: Calling FileUtil.copyFile where src and dest resolve to the same canonical path — identical paths, hardlinks, or symlinks pointing to the same target.
Common situations: Accidentally passing the same path for src and dest; dest computed via normalization that collapses to src; src and dest are symlinks/hardlinks to one inode; copy-into-same-file backup logic.
Related errors
- Files '{}' and '{}' are equal
- File not exist: {}
- File not exist: {}
- Src is a directory but dest is a file!
- Dest is a sub directory of src !
AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14).
Data as JSON: /api/errors/fe81434544882f54.
Report an issue: GitHub.