chinabugotech/hutool · error · IORuntimeException
Files '{}' and '{}' are equal
Error message
Files '{}' and '{}' are equal What it means
FileCopier.copy() checks whether src and dest are the same file via FileUtil.equals (canonical path). Copying a file onto itself would corrupt/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/file/FileCopier.java:179
* 6、源为目录,目标为文件,抛出IO异常
* 7、源路径和目标路径相同时,抛出IO异常
* </pre>
*
* @return 拷贝后目标的文件或目录
* @throws IORuntimeException IO异常
*/
@Override
public File copy() throws IORuntimeException{
final File src = this.src;
File dest = this.dest;
// 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 (FileUtil.equals(src, dest)) {
throw new IORuntimeException("Files '{}' and '{}' are equal", src, dest);
}
if (src.isDirectory()) {// 复制目录
if(dest.exists() && false == dest.isDirectory()) {
//源为目录,目标为文件,抛出IO异常
throw new IORuntimeException("Src is a directory but dest is a file!");
}
if(FileUtil.isSub(src, dest)) {
throw new IORuntimeException("Dest is a sub directory of src !");
}
final File subTarget = isCopyContentIfDir ? dest : FileUtil.mkdir(FileUtil.file(dest, src.getName()));
internalCopyDirContent(src, subTarget);
} else {// 复制文件
dest = internalCopyFile(src, dest);
}
return dest;
}View on GitHub (pinned to 8870454b2a)
Solutions
- Ensure dest is a different path from src.
- Generate a unique destination name (append timestamp/counter).
- Check FileUtil.equals(src, dest) before calling copy() and skip or rename.
Example fix
// before
FileCopier.create(file, file).copy(); // throws: equal
// after
File dest = new File(file.getParentFile(), "backup_" + file.getName());
if (!FileUtil.equals(file, dest)) {
FileCopier.create(file, dest).copy();
} Defensive patterns
Strategy: validation
Validate before calling
if (!FileUtil.equals(src, dest)) {
return FileCopier.create(src, dest).copy();
}
// same file: pick a different destination Type guard
static boolean areDistinctFiles(File a, File b) {
return a != null && b != null && !FileUtil.equals(a, b);
} Try / catch
try {
return FileCopier.create(src, dest).copy();
} catch (IORuntimeException e) {
if (e.getMessage().contains("are equal")) {
dest = new File(dest.getParentFile(), "copy_" + dest.getName());
return FileCopier.create(src, dest).copy();
}
throw e;
} Prevention
- Check FileUtil.equals(src, dest) before copying.
- Generate unique destination names for copies and backups.
- Account for symlinks/hardlinks that resolve distinct paths to one inode.
When it happens
Trigger: Calling copy() on a FileCopier whose src and dest resolve to the same canonical path — identical paths, hardlinks, or symlinks to the same inode.
Common situations: Same path passed for src and dest; dest computed via normalization that collapses to src; src and dest are symlinks/hardlinks to one inode; backup logic that targets the source location.
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/b79207c8e9f6d53a.
Report an issue: GitHub.