{"record":{"id":"2fe828d11c98440c","repo":"HKUDS/DeepTutor","slug":"note-path-name-r-is-not-valid-utf-8-byte-offset","errorCode":null,"errorMessage":"Note {path.name!r} is not valid UTF-8 (byte offset {exc.start}); editing it would corrupt the undecodable bytes. Fix its encoding first.","messagePattern":"Note (.+?) is not valid UTF-8 \\(byte offset (.+?)\\); editing it would corrupt the undecodable bytes\\. Fix its encoding first\\.","errorType":"exception","errorClass":"VaultError","httpStatus":null,"severity":"error","filePath":"deeptutor/capabilities/obsidian/vault.py","lineNumber":82,"sourceCode":"    Reads only. A read-modify-write must use :func:`_read_text_exact`, which\n    refuses to launder those bytes back onto disk.\n    \"\"\"\n    return path.read_text(encoding=\"utf-8\", errors=\"replace\")\n\n\ndef _read_text_exact(path: Path) -> str:\n    \"\"\"Decode a note that is about to be written back, without substitution.\n\n    Rewriting a note we could only partially decode would replace the user's\n    bytes with U+FFFD, so writes stay strict. Raising ``VaultError`` (rather\n    than letting ``UnicodeDecodeError`` escape) is what makes the refusal\n    legible: the tool layer turns it into a message naming the note and the\n    fix, instead of an opaque failure.\n    \"\"\"\n    try:\n        return path.read_text(encoding=\"utf-8\")\n    except UnicodeDecodeError as exc:\n        raise VaultError(\n            f\"Note {path.name!r} is not valid UTF-8 (byte offset {exc.start}); \"\n            \"editing it would corrupt the undecodable bytes. Fix its encoding first.\"\n        ) from exc\n\n\ndef _safe_join(root: Path, rel: str) -> Path:\n    \"\"\"Resolve ``rel`` under ``root``, refusing anything that escapes the vault.\"\"\"\n    root_resolved = root.resolve()\n    candidate = (root_resolved / rel.lstrip(\"/\")).resolve()\n    if candidate != root_resolved and root_resolved not in candidate.parents:\n        raise VaultError(f\"Path {rel!r} escapes the vault.\")\n    return candidate\n\n\ndef _with_md_suffix(rel: str) -> str:\n    return rel if rel.lower().endswith(\".md\") else f\"{rel}.md\"\n\n","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/capabilities/obsidian/vault.py#L64-L100","documentation":"Error \"Note {path.name!r} is not valid UTF-8 (byte offset {exc.start}); editing it would corrupt the undecodable bytes. Fix its encoding first.\" thrown in HKUDS/DeepTutor.","triggerScenarios":"Thrown at deeptutor/capabilities/obsidian/vault.py:82 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":[],"exampleFix":null,"handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}