{"record":{"id":"82dbb792e90844a2","repo":"MemPalace/mempalace","slug":"could-not-read-filepath-symlinked-files-are-ski","errorCode":null,"errorMessage":"Could not read {filepath}: symlinked files are skipped","messagePattern":"Could not read (.+?): symlinked files are skipped","errorType":"exception","errorClass":"IOError","httpStatus":null,"severity":"error","filePath":"mempalace/normalize.py","lineNumber":130,"sourceCode":"    # Narrow shape — a bare \"(ctrl+o to expand)\" in user prose stays intact.\n    text = re.sub(r\"\\s*\\[\\d+\\s+tokens?\\]\\s*\\(ctrl\\+o to expand\\)\", \"\", text)\n    # Collapse runs of blank lines created by the removals\n    text = re.sub(r\"\\n{4,}\", \"\\n\\n\\n\", text)\n    return text.strip()\n\n\ndef _read_transcript_file(filepath: str) -> str:\n    \"\"\"Read a transcript source file with the same safety checks normalize()\n    and normalize_conversations() both need: no symlinks, regular files only,\n    size-capped, BOM-tolerant.\n    \"\"\"\n    # O_NONBLOCK keeps the \"not a regular file\" check below reachable: a\n    # blocking open of a FIFO waits in the kernel for a writer, so the\n    # S_ISREG test never runs. See ``miner._read_text_no_follow``, including\n    # why the EAGAIN branch re-checks the type and retries without the flag.\n    flags = os.O_RDONLY | getattr(os, \"O_NOFOLLOW\", 0) | getattr(os, \"O_NONBLOCK\", 0)\n    if os.path.islink(filepath):\n        raise IOError(f\"Could not read {filepath}: symlinked files are skipped\")\n    fd = -1\n    try:\n        try:\n            fd = os.open(filepath, flags)\n        except OSError as exc:\n            if exc.errno != errno.EAGAIN or not stat.S_ISREG(os.lstat(filepath).st_mode):\n                raise\n            fd = os.open(filepath, flags & ~getattr(os, \"O_NONBLOCK\", 0))\n        file_stat = os.fstat(fd)\n        if not stat.S_ISREG(file_stat.st_mode):\n            # Text stays prefix-free: this raise is inside the ``try``, so the\n            # ``except OSError`` below composes \"Could not read <path>: ...\".\n            raise IOError(\"not a regular file\")\n        if file_stat.st_size > 500 * 1024 * 1024:  # 500 MB safety limit\n            # Prefix-free for the same reason as the branch above.\n            raise IOError(f\"file too large ({file_stat.st_size // (1024 * 1024)} MB)\")\n        with os.fdopen(fd, \"r\", encoding=\"utf-8-sig\", errors=\"replace\") as f:\n            fd = -1","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/normalize.py#L112-L148","documentation":"Error \"Could not read {filepath}: symlinked files are skipped\" thrown in MemPalace/mempalace.","triggerScenarios":"Thrown at mempalace/normalize.py:130 when the library encounters an invalid state.","commonSituations":"Symlinked transcript files are skipped by design.","solutions":["Point at the real file instead of a symlink, or copy the content to a regular file"],"exampleFix":null,"handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}