{"record":{"id":"1e7325b1d5dfb835","repo":"binary-husky/gpt_academic","slug":"not-a-file-path-1e7325","errorCode":null,"errorMessage":"Not a file: {path}","messagePattern":"Not a file: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"crazy_functions/doc_fns/read_fns/unstructured_all/unstructured_reader.py","lineNumber":118,"sourceCode":"\n        Args:\n            file_path: 文件路径\n            max_size_mb: 允许的最大文件大小(MB)\n\n        Returns:\n            Path: 验证后的Path对象\n\n        Raises:\n            ValueError: 文件不存在、格式不支持或大小超限\n            PermissionError: 没有读取权限\n        \"\"\"\n        path = Path(file_path).resolve()\n\n        if not path.exists():\n            raise ValueError(f\"File not found: {path}\")\n\n        if not path.is_file():\n            raise ValueError(f\"Not a file: {path}\")\n\n        if not os.access(path, os.R_OK):\n            raise PermissionError(f\"No read permission: {path}\")\n\n        file_size_mb = path.stat().st_size / (1024 * 1024)\n        if file_size_mb > max_size_mb:\n            raise ValueError(\n                f\"File size ({file_size_mb:.1f}MB) exceeds limit of {max_size_mb}MB\"\n            )\n\n        if path.suffix.lower() not in self.SUPPORTED_EXTENSIONS:\n            raise ValueError(\n                f\"Unsupported format: {path.suffix}. \"\n                f\"Supported: {', '.join(sorted(self.SUPPORTED_EXTENSIONS))}\"\n            )\n\n        return path\n","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/binary-husky/gpt_academic/blob/d6bde0fa54373309bd05823a49bda8da019d2c77/crazy_functions/doc_fns/read_fns/unstructured_all/unstructured_reader.py#L100-L136","documentation":"Raised by UnstructuredReader._validate_file when the path exists but is not a regular file (path.is_file() is False) — i.e. it is a directory, a symlink to a directory, or a special file (fifo/socket/device). Distinct from 'File not found' which fires earlier for missing paths.","triggerScenarios":"Passing a directory path to the reader (e.g. pointing at a folder of PDFs instead of one file); passing /dev/null or a named pipe; passing a symlink chain that ends at a directory; passing a broken special path like a Windows junction.","commonSituations":"UI accepting either a file or folder and forwarding it unchecked; glob patterns that match a directory ('data' instead of 'data/*.pdf'); symlinks in shared storage pointing at folders; automated pipelines iterating os.listdir output that includes subdirectories.","solutions":["Filter to regular files before calling: iterate p for p in Path(d).iterdir() if p.is_file().","If a directory was intended, expand it to its supported files and call the reader per file.","Resolve symlinks (Path.resolve()) and re-check is_file() to catch links to directories.","Validate user-supplied paths against a whitelist root to reject odd paths early."],"exampleFix":"# before\nreader.read(user_path)  # ValueError if user_path is a directory\n\n# after\np = Path(user_path).resolve()\npaths = [c for c in p.iterdir() if c.is_file()] if p.is_dir() else [p]\ntexts = [reader.read(c) for c in paths]","handlingStrategy":"type-guard","validationCode":"from pathlib import Path\np = Path(user_path).resolve()\npaths = sorted(p.iterdir()) if p.is_dir() else [p]\npaths = [c for c in paths if c.is_file()]","typeGuard":"def is_regular_file(path: str) -> bool:\n    from pathlib import Path\n    return Path(path).is_file()","tryCatchPattern":"try:\n    reader.read(fp)\nexcept ValueError as e:\n    if str(e).startswith('Not a file'):\n        expand_dir_to_files(fp)  # user passed a folder\n    raise","preventionTips":["Expand directories to file lists before processing.","Resolve() symlinks and re-check is_file().","Validate user paths against a whitelist root.","Use p.iterdir() filters with is_file() in batch loops."],"tags":["filesystem","validation","path","python"],"backgroundTag":null,"analyzedSha":"d6bde0fa54373309bd05823a49bda8da019d2c77","analyzedAt":"2026-08-14T22:48:35.038Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}