chinabugotech/hutool · error · IORuntimeException
Dest is a sub directory of src !
Error message
Dest is a sub directory of src !
What it means
FileCopier.copy() detects when dest is a subdirectory of src via FileUtil.isSub. Copying a directory into its own descendant would recurse infinitely (the copy keeps finding newly created children), so it throws IORuntimeException("Dest is a sub directory of src !").
Source
Thrown at hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java:188
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>
* 拷贝内容的意思为源目录下的所有文件和目录拷贝到另一个目录下,而不拷贝源目录本身
*
* @param src 源目录
* @param dest 目标目录
* @throws IORuntimeException IO异常View on GitHub (pinned to 8870454b2a)
Solutions
- Choose a dest that is outside the src directory tree.
- Copy to a sibling or ancestor location first, then move if needed.
- Check FileUtil.isSub(src, dest) before copy() and reject early.
Example fix
// before
FileCopier.create(new File("/a"), new File("/a/b")).copy(); // throws: sub dir
// after: copy outside the tree, then relocate
File staging = new File("/tmp/staging_b");
FileCopier.create(new File("/a"), staging).copy();
// optionally move staging into /a after copy completes Defensive patterns
Strategy: validation
Validate before calling
if (src.isDirectory() && FileUtil.isSub(src, dest)) {
// dest is inside src: choose an outside target
throw new IllegalArgumentException("dest must not be inside src");
}
return FileCopier.create(src, dest).copy(); Type guard
static boolean isSafeCopyTarget(File src, File dest) {
return !(src != null && src.isDirectory() && FileUtil.isSub(src, dest));
} Try / catch
try {
return FileCopier.create(src, dest).copy();
} catch (IORuntimeException e) {
if (e.getMessage().contains("sub directory of src")) {
// dest is inside src: copy to an outside staging dir first
}
throw e;
} Prevention
- Never set dest to a path inside src when copying a directory.
- Copy to a sibling or external staging location, then move if needed.
- Check FileUtil.isSub(src, dest) before copying directories.
When it happens
Trigger: Calling copy() on a FileCopier where dest is inside src — e.g. src=/a, dest=/a/b.
Common situations: Copying a folder into itself or one of its children; a backup-into-source mistake; dest computed as a subpath of src.
Related errors
- File not exist: {}
- Files '{}' and '{}' are equal
- File not exist: {}
- Files '{}' and '{}' are equal
- Src is a directory but dest is a file!
AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14).
Data as JSON: /api/errors/c3480503f875a48a.
Report an issue: GitHub.