anthropics/skills · error · ValueError

relationship target escapes the package: {target!r}

Error message

relationship target escapes the package: {target!r}

What it means

opc_target() resolves relationship targets against the source part and rejects any that walk above the package root: when a '..' segment pops an empty segment stack, the target escapes the OPC package. This guards against both malformed files and deliberate ../.. path traversal inside .rels.

Source

Thrown at skills/xlsx/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

  1. Unzip the file and inspect the Target values in the .rels named in the error — anything starting with ../ chains is the culprit
  2. Discard files from untrusted sources that trigger this; it is a traversal red flag, not something to patch around
  3. If you generate packages, compute targets with posixpath.relpath against the correct source part and assert they stay under the root
Defensive patterns

Strategy: try-catch

Validate before calling

import posixpath

def target_stays_in_package(target: str, source_part: str) -> bool:
    if target.startswith("/"):
        joined = target.lstrip("/")
    else:
        joined = posixpath.join(posixpath.dirname(source_part), target)
    depth = 0
    for seg in posixpath.normpath(joined).split("/"):
        if seg in ("", "."):
            continue
        depth = depth - 1 if seg == ".." else depth + 1
        if depth < 0:
            return False
    return True

Try / catch

try:
    part = opc_target(target, source_part, mode)
except ValueError as e:
    if "escapes the package" in str(e):
        raise SuspiciousDocument(source_part, target) from e  # quarantine, don't sanitize
    raise

Prevention

When it happens

Trigger: Loading a package whose .rels has Target="../../../../etc/passwd" or any target that, resolved relative to the source part (e.g. xl/worksheets + ../..), leaves the package root; also absolute-looking targets that normalize to a parent traversal.

Common situations: Malicious or corrupt Office files (zip-slip via relationships); documents generated by buggy writers computing relative paths from the wrong base; merging .rels from different packages without recomputing targets.

Related errors


AI-assisted analysis of anthropics/skills@f6656c1256 (2026-08-14). Data as JSON: /api/errors/781286c0d58e6154. Report an issue: GitHub.