{"record":{"id":"c13fbdc3dc9792f1","repo":"alibaba/canal","slug":"failed-to-read-file","errorCode":null,"errorMessage":"Failed to read file","messagePattern":"Failed to read file","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"common/src/main/java/com/alibaba/otter/canal/common/utils/FileUtils.java","lineNumber":112,"sourceCode":"     *\n     * @param baseDir\n     * @param destination\n     * @return\n     */\n    public static String validateFileName(String baseDir, String destination) {\n        try {\n            // 验证 destination 是否在允许的基目录范围内\n            String basePath = new File(baseDir).getCanonicalPath();\n            String fullPath = new File(basePath, destination).getCanonicalPath();\n\n            // 检查 fullPath 是否以 basePath 开头\n            if (!fullPath.startsWith(basePath + File.separator)) {\n                throw new IllegalArgumentException(\"Invalid destination path\");\n            }\n\n            return fullPath;\n        } catch (IOException e) {\n            throw new RuntimeException(\"Failed to read file\", e);\n        }\n    }\n\n    public static void main(String[] args) throws IOException {\n        String fullPath = validateFileName(\"/tmp/\", \"1.txt\");\n        System.out.println(fullPath);\n        System.out.println(org.apache.commons.io.FileUtils.readLines(new File(fullPath)));\n\n        fullPath = validateFileName(\"/tmp/\", \"test\");\n        fullPath = validateFileName(fullPath,\"1.txt\");\n        System.out.println(fullPath);\n        System.out.println(org.apache.commons.io.FileUtils.readLines(new File(fullPath)));\n\n\n        fullPath = validateFileName(\"/tmp/\", \"../etc/hosts\");\n        System.out.println(fullPath);\n        System.out.println(org.apache.commons.io.FileUtils.readLines(new File(fullPath)));\n    }","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/alibaba/canal/blob/87be50e87686a3e8af08c368d0e1ffd1f59eb04a/common/src/main/java/com/alibaba/otter/canal/common/utils/FileUtils.java#L94-L130","documentation":"Thrown by FileUtils.validateFileName() when getCanonicalPath() throws an IOException during path resolution. This wraps the underlying I/O error (which may be caused by the base directory not existing, a broken symlink, permission denied, or filesystem-level errors) rather than the path-traversal check itself.","triggerScenarios":"Calling validateFileName() where either baseDir or basePath+destination cannot be resolved to a canonical path — e.g. baseDir does not exist, a path component is a dangling symlink, or the JVM lacks read/execute permission on a directory in the path.","commonSituations":"The configured canal data/conf directory does not exist at startup; permissions were changed after deployment; a symlink target was deleted; running in a container where the volume mount path is incorrect or not mounted.","solutions":["Verify that baseDir exists and is accessible: check with `ls -la baseDir` and confirm the JVM process has read/execute permissions.","Ensure all symlinks in the path chain resolve to existing targets.","In containerized deployments, confirm volume mounts are correctly configured and the path matches the canal configuration.","Check the canal.instance/conf directory path in canal.properties — correct it if the deployment layout differs from the default."],"exampleFix":"// before — baseDir does not exist\ncanal.conf.dir = /opt/canal/conf  // missing or not mounted\n\n// after — correct path\ncanal.conf.dir = /home/admin/canal-server/conf","handlingStrategy":"try-catch","validationCode":"// Verify baseDir exists and is accessible before calling validateFileName\nFile base = new File(baseDir);\nif (!base.exists() || !base.isDirectory()) {\n    throw new FileNotFoundException(\"Base directory does not exist: \" + baseDir);\n}\nif (!base.canRead()) {\n    throw new SecurityException(\"Cannot read base directory: \" + baseDir);\n}","typeGuard":"null","tryCatchPattern":"try {\n    String path = FileUtils.validateFileName(baseDir, destination);\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Failed to read file\")) {\n        logger.error(\"Cannot resolve canonical path for baseDir={}, dest={}\", baseDir, destination, e);\n    }\n    throw e;\n}","preventionTips":["Verify the base directory exists and is readable before canal startup.","In containerized deployments, confirm volume mounts are present and paths match config.","Check file permissions for the JVM process user on all path components.","Validate paths in health checks to catch missing directories early."],"tags":["filesystem","io-error","permission","canal-common"],"backgroundTag":null,"analyzedSha":"87be50e87686a3e8af08c368d0e1ffd1f59eb04a","analyzedAt":"2026-08-14T04:30:11.918Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}