{"record":{"id":"aaf39366c5e7c115","repo":"subframe7536/maple-font","slug":"error-reading-file-file-path-e","errorCode":null,"errorMessage":"Error reading file: {file_path} - {e}","messagePattern":"Error reading file: (.+?) - (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"source/py/utils.py","lineNumber":278,"sourceCode":"    return sha256.hexdigest(), zip_name_without_ext\n\n\ndef get_directory_hash(dir_path: str) -> str:\n    hasher = hashlib.sha256()\n    for root, _, files in sorted(walk(dir_path)):\n        for file in sorted(files):\n            file_path = path.join(root, file)\n            try:\n                with open(file_path, \"rb\") as f:\n                    while True:\n                        # 4KB chunk size\n                        chunk = f.read(4096)\n                        if not chunk:\n                            break\n                        hasher.update(chunk)\n\n            except (IOError, OSError) as e:\n                raise Exception(f\"Error reading file: {file_path} - {e}\")\n\n    return hasher.hexdigest()\n\n\ndef check_directory_hash(dir_path: str, hash_path: str | None = None) -> bool:\n    if not path.exists(dir_path):\n        print(f\"{dir_path} not exist, skip computing hash\")\n        return False\n    with open(hash_path or f\"{dir_path}.sha256\", \"r\") as f:\n        return f.readline() == get_directory_hash(dir_path)\n\n\ndef merge_ttfonts(\n    base_font_path: str, extra_font_path: str, use_pyftmerge: bool = False\n) -> TTFont:\n    \"\"\"\n    Merge glyphs from ``source_font`` into ``base_font``, skipping duplicate glyph names.\n","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/subframe7536/maple-font/blob/c08fda97fef73d68c1755219852150770f6e6578/source/py/utils.py#L260-L296","documentation":"get_directory_hash walks a directory and hashes each file in 4KB chunks. If any file cannot be read due to an IOError/OSError (permission denied, file vanished mid-walk, too many open files, etc.), it wraps the OS error in this Exception with the file path. It is thrown so update_dir_hash / check_directory_hash callers get one consistent error type naming the offending file.","triggerScenarios":"Calling update_dir_hash(dir_path) or check_directory_hash(dir_path) when any file under dir_path cannot be opened for reading: unreadable permissions, a broken symlink, a file deleted between directory scan and read, or an unreadable special file.","commonSituations":"Running the build in a container/CI where the output dir contains root-owned files; antivirus or another process deleting files mid-hash; hashing a directory containing sockets/fifos; hitting the open-file limit with many files.","solutions":["Fix permissions on the reported file (chmod/chown) or rerun the command with sufficient privileges.","Remove or exclude unreadable/special files (symlinks, sockets) from the hashed directory.","Rerun the build if the file was transiently deleted; stop processes racing on the directory.","Raise the open-file limit (ulimit -n) if the directory contains very many files."],"exampleFix":"// before\n# update_dir_hash('/build/output')  # PermissionError wrapped\n// after (shell)\n# sudo chmod -R u+rw /build/output && python build.py","handlingStrategy":"try-catch","validationCode":"import os\n\ndef assert_dir_readable(dir_path):\n    for root, _, files in os.walk(dir_path):\n        for f in files:\n            p = os.path.join(root, f)\n            if not os.path.isfile(p) or os.path.islink(p):\n                continue\n            with open(p, \"rb\"):\n                pass  # raises PermissionError before hashing starts","typeGuard":"import os, stat\n\ndef is_readable_regular_file(path: str) -> bool:\n    try:\n        st = os.stat(path)\n        return stat.S_ISREG(st.st_mode) and os.access(path, os.R_OK)\n    except OSError:\n        return False","tryCatchPattern":"try:\n    h = update_dir_hash(dir_path)\nexcept Exception as e:\n    if str(e).startswith(\"Error reading file:\"):\n        print(\"Fix permissions or remove unreadable file:\", e)\n    raise","preventionTips":["Run builds as a user with read access to all build inputs/outputs.","Exclude special files (sockets, fifos, broken symlinks) from hashed dirs.","Avoid concurrent processes deleting files in the hashed directory.","Raise ulimit -n when hashing directories with very many files."],"tags":["filesystem","io","hashing"],"backgroundTag":"file-read-permission-denied","analyzedSha":"c08fda97fef73d68c1755219852150770f6e6578","analyzedAt":"2026-08-28T22:07:29.503Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}