chinabugotech/hutool · error · IORuntimeException

Src is a directory but dest is a file!

Error message

Src is a directory but dest is a file!

What it means

When src is a directory, FileCopier.copy() requires dest to be a directory as well (or non-existent so it can be created). If dest already exists and is a regular file, the copy is a type mismatch and it throws IORuntimeException("Src is a directory but dest is a file!").

Source

Thrown at hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java:185

	 */
	@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;
	}

	//----------------------------------------------------------------------------------------- Private method start
	/**
	 * 拷贝目录内容,只用于内部,不做任何安全检查<br>
	 * 拷贝内容的意思为源目录下的所有文件和目录拷贝到另一个目录下,而不拷贝源目录本身
	 *

View on GitHub (pinned to 8870454b2a)

Solutions

  1. Delete or rename the existing dest file before copying a directory onto it.
  2. Choose a dest that is a directory or does not exist yet.
  3. Check dest type before copy() and route directories vs files differently.

Example fix

// before
FileCopier.create(srcDir, existingFile).copy(); // throws: type mismatch

// after
if (dest.exists() && dest.isFile()) {
    FileUtil.del(dest); // or rename
}
FileCopier.create(srcDir, dest).copy();
Defensive patterns

Strategy: validation

Validate before calling

if (src.isDirectory() && dest.exists() && dest.isFile()) {
    FileUtil.del(dest); // or rename aside
}
return FileCopier.create(src, dest).copy();

Type guard

static boolean compatibleCopyTargets(File src, File dest) {
    if (src == null || dest == null) return false;
    if (src.isDirectory() && dest.exists() && dest.isFile()) return false;
    return true;
}

Try / catch

try {
    return FileCopier.create(src, dest).copy();
} catch (IORuntimeException e) {
    if (e.getMessage().contains("Src is a directory but dest is a file")) {
        FileUtil.del(dest);
        return FileCopier.create(src, dest).copy();
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling copy() on a FileCopier where src is a directory and dest is an existing regular file.

Common situations: Copying a folder into a location already occupied by a file; dest path points at an existing file; configuration mistake where src is a directory but dest is a file path.

Related errors


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