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
- Delete or rename the existing dest file before copying a directory onto it.
- Choose a dest that is a directory or does not exist yet.
- 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
- Check dest type before copying a directory onto it.
- Remove or rename a conflicting dest file first.
- Route directory vs file copies through separate logic.
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
- File not exist: {}
- Files '{}' and '{}' are equal
- File not exist: {}
- Files '{}' and '{}' are equal
- Dest is a sub directory of src !
AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14).
Data as JSON: /api/errors/2248b3c9a7fd6dc4.
Report an issue: GitHub.