{"record":{"id":"6200321640bb6580","repo":"datawhalechina/hello-agents","slug":"e-620032","errorCode":null,"errorMessage":"无法读取学习计划：{e}","messagePattern":"无法读取学习计划：(.+?)","errorType":"exception","errorClass":"FileReadError","httpStatus":null,"severity":"error","filePath":"Co-creation-projects/Yixiang-Wu-LearningAgent/core/file_manager.py","lineNumber":119,"sourceCode":"        读取学习计划\n\n        Args:\n            domain: 领域名称\n\n        Returns:\n            计划内容\n\n        Raises:\n            FileNotFoundError: 如果计划不存在\n        \"\"\"\n        plan_path = self.BASE_DIR / domain / \"plan.md\"\n        if not plan_path.exists():\n            raise FileNotFoundError(f\"学习计划不存在：{domain}\")\n\n        try:\n            return plan_path.read_text(encoding=\"utf-8\")\n        except Exception as e:\n            raise FileReadError(f\"无法读取学习计划：{e}\")\n\n    def domain_exists(self, domain: str) -> bool:\n        \"\"\"\n        检查领域是否存在\n\n        Args:\n            domain: 领域名称\n\n        Returns:\n            是否存在\n        \"\"\"\n        return (self.BASE_DIR / domain).exists()\n\n    def list_domains(self) -> List[str]:\n        \"\"\"\n        列出所有学习领域\n\n        Returns:","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/Yixiang-Wu-LearningAgent/core/file_manager.py#L101-L137","documentation":"FileReadError raised by FileManager.read_plan when plan.md exists but read_text fails. Typical causes: permission denied on the file, an undecodable byte sequence under strict UTF-8 decoding (UnicodeDecodeError), or the path being removed between the exists() check and the read (TOCTOU race).","triggerScenarios":"plan.md written by another tool in GBK/Latin-1 encoding then read with encoding='utf-8'; file owned by another user with mode 600; file deleted concurrently after the exists() check; BASE_DIR on a network mount that returns I/O errors.","commonSituations":"Notes edited on Windows with a legacy-encoding editor; files created by root in a container then read by an unprivileged process; concurrent agent processes pruning the workspace.","solutions":["If encoding is suspect, read bytes and decode with errors='replace' or detect encoding, or write everything as UTF-8 consistently","Fix file ownership/permissions (chmod/chown) so the reading process can access it","Drop the exists() pre-check and let the try block handle FileNotFoundError separately, removing the race","Catch OSError specifically and chain with `from e`"],"exampleFix":"# before\nif not plan_path.exists():\n    raise FileNotFoundError(f\"学习计划不存在：{domain}\")\ntry:\n    return plan_path.read_text(encoding=\"utf-8\")\nexcept Exception as e:\n    raise FileReadError(f\"无法读取学习计划：{e}\")\n\n# after\ntry:\n    return plan_path.read_text(encoding=\"utf-8\")\nexcept FileNotFoundError:\n    raise FileNotFoundError(f\"学习计划不存在：{domain}\") from None\nexcept UnicodeDecodeError as e:\n    raise FileReadError(f\"plan.md 不是有效 UTF-8: {e}\") from e\nexcept OSError as e:\n    raise FileReadError(f\"无法读取学习计划: {e}\") from e","handlingStrategy":"try-catch","validationCode":"def plan_readable(plan_path: Path) -> bool:\n    try:\n        return plan_path.is_file() and os.access(plan_path, os.R_OK)\n    except OSError:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    plan = fm.read_plan(domain)\nexcept FileNotFoundError:\n    ...  # create default\nexcept FileReadError as e:\n    if 'UnicodeDecodeError' in str(e) or 'codec' in str(e):\n        # re-read leniently\n        plan = (BASE_DIR / domain / 'plan.md').read_bytes().decode('utf-8', 'replace')\n    else:\n        raise","preventionTips":["Write every file with encoding='utf-8' (this code does) and never edit generated notes with legacy-encoding editors","Separate FileNotFoundError from FileReadError in the handler — they need different recovery paths","Remove the exists() pre-check so the read is the single source of truth (avoids TOCTOU)"],"tags":["filesystem","encoding","io","python","race-condition","learning-agent"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}