{"record":{"id":"b79207c8e9f6d53a","repo":"chinabugotech/hutool","slug":"files-and-are-equal-b79207","errorCode":null,"errorMessage":"Files '{}' and '{}' are equal","messagePattern":"Files '(.+?)' and '(.+?)' are equal","errorType":"exception","errorClass":"IORuntimeException","httpStatus":null,"severity":"error","filePath":"hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java","lineNumber":179,"sourceCode":"\t * 6、源为目录，目标为文件，抛出IO异常\n\t * 7、源路径和目标路径相同时，抛出IO异常\n\t * </pre>\n\t *\n\t * @return 拷贝后目标的文件或目录\n\t * @throws IORuntimeException IO异常\n\t */\n\t@Override\n\tpublic File copy() throws IORuntimeException{\n\t\tfinal File src = this.src;\n\t\tFile dest = this.dest;\n\t\t// check\n\t\tAssert.notNull(src, \"Source File is null !\");\n\t\tif (false == src.exists()) {\n\t\t\tthrow new IORuntimeException(\"File not exist: \" + src);\n\t\t}\n\t\tAssert.notNull(dest, \"Destination File or directiory is null !\");\n\t\tif (FileUtil.equals(src, dest)) {\n\t\t\tthrow new IORuntimeException(\"Files '{}' and '{}' are equal\", src, dest);\n\t\t}\n\n\t\tif (src.isDirectory()) {// 复制目录\n\t\t\tif(dest.exists() && false == dest.isDirectory()) {\n\t\t\t\t//源为目录，目标为文件，抛出IO异常\n\t\t\t\tthrow new IORuntimeException(\"Src is a directory but dest is a file!\");\n\t\t\t}\n\t\t\tif(FileUtil.isSub(src, dest)) {\n\t\t\t\tthrow new IORuntimeException(\"Dest is a sub directory of src !\");\n\t\t\t}\n\n\t\t\tfinal File subTarget = isCopyContentIfDir ? dest : FileUtil.mkdir(FileUtil.file(dest, src.getName()));\n\t\t\tinternalCopyDirContent(src, subTarget);\n\t\t} else {// 复制文件\n\t\t\tdest = internalCopyFile(src, dest);\n\t\t}\n\t\treturn dest;\n\t}","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/chinabugotech/hutool/blob/8870454b2a0c29cc6ffd31dcf5667c8ceb2fc442/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java#L161-L197","documentation":"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.","triggerScenarios":"Calling copy() on a FileCopier whose src and dest resolve to the same canonical path — identical paths, hardlinks, or symlinks to the same inode.","commonSituations":"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.","solutions":["Ensure dest is a different path from src.","Generate a unique destination name (append timestamp/counter).","Check FileUtil.equals(src, dest) before calling copy() and skip or rename."],"exampleFix":"// before\nFileCopier.create(file, file).copy(); // throws: equal\n\n// after\nFile dest = new File(file.getParentFile(), \"backup_\" + file.getName());\nif (!FileUtil.equals(file, dest)) {\n    FileCopier.create(file, dest).copy();\n}","handlingStrategy":"validation","validationCode":"if (!FileUtil.equals(src, dest)) {\n    return FileCopier.create(src, dest).copy();\n}\n// same file: pick a different destination","typeGuard":"static boolean areDistinctFiles(File a, File b) {\n    return a != null && b != null && !FileUtil.equals(a, b);\n}","tryCatchPattern":"try {\n    return FileCopier.create(src, dest).copy();\n} catch (IORuntimeException e) {\n    if (e.getMessage().contains(\"are equal\")) {\n        dest = new File(dest.getParentFile(), \"copy_\" + dest.getName());\n        return FileCopier.create(src, dest).copy();\n    }\n    throw e;\n}","preventionTips":["Check FileUtil.equals(src, dest) before copying.","Generate unique destination names for copies and backups.","Account for symlinks/hardlinks that resolve distinct paths to one inode."],"tags":["io","file","copy","data-safety"],"backgroundTag":null,"analyzedSha":"8870454b2a0c29cc6ffd31dcf5667c8ceb2fc442","analyzedAt":"2026-08-14T04:01:12.892Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}