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

  1. Ensure dest is a different path from src.
  2. Generate a unique destination name (append timestamp/counter).
  3. 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

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


AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14). Data as JSON: /api/errors/b79207c8e9f6d53a. Report an issue: GitHub.