{"record":{"id":"ad071afc744b8ebc","repo":"anthropics/skills","slug":"relationship-target-is-not-a-posix-part-name-tar","errorCode":null,"errorMessage":"relationship target is not a POSIX part name: {target!r}","messagePattern":"relationship target is not a POSIX part name: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/docx/scripts/office/helpers/__init__.py","lineNumber":35,"sourceCode":"}\n\n_SCHEME_RE = re.compile(r\"^[A-Za-z][A-Za-z0-9+.\\-]*:\")\n\nSLIDE_REL_TYPE = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide\"\n\n\ndef opc_target(target: str, source_part: str, target_mode: str = \"\") -> str | None:\n    if not target:\n        return None\n    if target_mode.lower() == \"external\":\n        return None\n    if _SCHEME_RE.match(target):\n        return None\n\n    target = urllib.parse.unquote(target)\n\n    if \"\\\\\" in target:\n        raise ValueError(f\"relationship target is not a POSIX part name: {target!r}\")\n\n    if target.startswith(\"/\"):\n        joined = target.lstrip(\"/\")\n    else:\n        joined = posixpath.join(posixpath.dirname(source_part), target)\n\n    parts: list[str] = []\n    for segment in posixpath.normpath(joined).split(\"/\"):\n        if segment in (\"\", \".\"):\n            continue\n        if segment == \"..\":\n            if not parts:\n                raise ValueError(f\"relationship target escapes the package: {target!r}\")\n            parts.pop()\n        else:\n            parts.append(segment)\n\n    if not parts:","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/anthropics/skills/blob/f6656c1256d5a8adfa37db9110046ef20bac644c/skills/docx/scripts/office/helpers/__init__.py#L17-L53","documentation":"opc_target() normalizes OOXML relationship targets into POSIX part names. It rejects any target containing a backslash, because OPC part names use forward slashes only; a backslash means the producer wrote a Windows-style or malformed path, and silently normalizing it could point at the wrong part. Thrown by both the docx and pptx copies of office/helpers/__init__.py.","triggerScenarios":"Any relationship in a .rels file whose Target attribute contains '\\\\' — e.g. Target=\"..\\\\theme\\\\theme1.xml\" or Target=\"media\\\\image1.png\", produced by non-conforming generators or hand-edited packages, when the package is unpacked and scanned (thumbnail.py get_slide_info, clean.py referenced-file walk, comment.py flows).","commonSituations":"PPTX/DOCX files generated by third-party tools (older converters, some report generators) that write Windows paths into .rels; packages that were unzipped/rezipped on Windows with path mangling; malicious or fuzzed inputs.","solutions":["Inspect the offending .rels file (message includes the target) and rewrite backslashes to forward slashes: Target=\"media/image1.png\".","Re-save the file with PowerPoint/Word or LibreOffice, which rewrite conformant relationships.","If you must process such files programmatically, pre-normalize targets with target.replace('\\\\', '/') before calling opc_target (only as a repair step, not silently).","Report the non-conforming file to whatever generator produced it."],"exampleFix":"<!-- before: ppt/slides/_rels/slide1.xml.rels -->\n<Relationship Id=\"rId2\" Type=\".../image\" Target=\"..\\\\media\\\\image1.png\"/>\n\n<!-- after -->\n<Relationship Id=\"rId2\" Type=\".../image\" Target=\"../media/image1.png\"/>","handlingStrategy":"validation","validationCode":"import zipfile, re\n\ndef rels_targets_ok(path: str) -> tuple[bool, str | None]:\n    with zipfile.ZipFile(path) as zf:\n        for name in zf.namelist():\n            if not name.endswith(\".rels\"):\n                continue\n            for m in re.finditer(rb'Target=\"([^\"]*)\"', zf.read(name)):\n                t = m.group(1).decode()\n                if \"\\\\\" in t:\n                    return False, f\"{name}: backslash target {t!r}\"\n    return True, None","typeGuard":null,"tryCatchPattern":"try:\n    opc_target(target, source_part, target_mode)\nexcept ValueError as e:\n    if \"not a POSIX part name\" in str(e):\n        opc_target(target.replace(\"\\\\\", \"/\"), source_part, target_mode)  # explicit repair, log it\n    else:\n        raise","preventionTips":["Validate .rels targets for backslashes before running the pipeline.","Only accept OOXML from conformant producers (Office, LibreOffice, python-pptx/openpyxl).","Log the offending target verbatim for repair.","Treat repeated occurrences as a signal to fix the upstream generator."],"tags":["ooxml","opc","relationships","validation","docx","pptx"],"backgroundTag":null,"analyzedSha":"f6656c1256d5a8adfa37db9110046ef20bac644c","analyzedAt":"2026-08-14T16:09:17.493Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}