anthropics/skills · error · ValueError
relationship target escapes the package: {target!r}
Error message
relationship target escapes the package: {target!r} What it means
pptx copy of the package-escape guard (same as error 4): opc_target() rejects relationship targets whose normalized path climbs above the package root via '..'. OPC part names cannot address anything outside the package, so such a target is malformed (or a traversal attempt) and resolution aborts.
Source
Thrown at skills/pptx/scripts/office/helpers/__init__.py:48
return None
target = urllib.parse.unquote(target)
if "\\" in target:
raise ValueError(f"relationship target is not a POSIX part name: {target!r}")
if target.startswith("/"):
joined = target.lstrip("/")
else:
joined = posixpath.join(posixpath.dirname(source_part), target)
parts: list[str] = []
for segment in posixpath.normpath(joined).split("/"):
if segment in ("", "."):
continue
if segment == "..":
if not parts:
raise ValueError(f"relationship target escapes the package: {target!r}")
parts.pop()
else:
parts.append(segment)
if not parts:
raise ValueError(f"relationship target resolves to nothing: {target!r}")
return "/".join(parts)
def rels_source_part(rels_file: Path, unpacked_dir: Path) -> str:
owner_dir = rels_file.parent.parent.relative_to(unpacked_dir)
return posixpath.join(owner_dir.as_posix(), rels_file.name[: -len(".rels")]).lstrip("./")
def part_text(data: bytes) -> str:
return data.decode("utf-8", "surrogateescape")
View on GitHub (pinned to f6656c1256)
Solutions
- Fix the target to stay inside the package — prefer a package-absolute target starting with '/' (e.g. '/ppt/media/image1.png').
- Remember relative targets resolve from the owning part's directory (e.g. ppt/slides/_rels → one '..' reaches ppt/).
- For untrusted input, quarantine: this shape often signals an attack, not an accident.
- Regenerate the deck with a conformant tool.
Example fix
<!-- before: ppt/_rels/presentation.xml.rels --> <Relationship Id="rId9" Type=".../slideMaster" Target="../../../../ppt/slideMasters/slideMaster1.xml"/> <!-- after --> <Relationship Id="rId9" Type=".../slideMaster" Target="/ppt/slideMasters/slideMaster1.xml"/>
Defensive patterns
Strategy: validation
Validate before calling
import posixpath
def target_stays_in_package(target: str, source_part: str) -> bool:
if not target or target.startswith("/"):
return True
joined = posixpath.normpath(posixpath.join(posixpath.dirname(source_part), target))
return not joined.startswith("..") and "/.." not in joined Try / catch
try:
opc_target(target, source_part, target_mode)
except ValueError as e:
if "escapes the package" in str(e):
log.security("traversal rel target %r in %s — skipping", target, source_part)
skip_relationship()
else:
raise Prevention
- Use package-absolute targets (leading '/') in generated .rels.
- Treat escape targets in untrusted decks as hostile.
- Validate packages with an OOXML validator before pipelines run.
When it happens
Trigger: A relationship in a PPTX .rels file with a target like '../../../../tmp/x' or an over-deep '../../' relative to the owning part; reached via thumbnail.py's slide-list build or clean.py's reference walk.
Common situations: Malicious/fuzzed decks with zip-slip-style rels targets; hand-authored .rels with wrong '..' counts relative to the source part; generators emitting absolute-ish traversal prefixes.
Related errors
- relationship target escapes the package: {target!r}
- relationship target is not a POSIX part name: {target!r}
- relationship target resolves to nothing: {target!r}
- symlink archive entry not allowed: {m.filename!r}
- unsafe archive entry: {m.filename!r}
AI-assisted analysis of anthropics/skills@f6656c1256 (2026-08-14).
Data as JSON: /api/errors/5361a93e011edfd7.
Report an issue: GitHub.