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

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

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


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