unslothai/unsloth · error · ValueError

path may not contain '..' segments: {raw!r}

Error message

path may not contain '..' segments: {raw!r}

What it means

Raised by resolve_under_root in storage_roots.py:412-414 when the path contains a '..' parent segment (checked textually on the raw string and via the expanded Path). This blocks trivial traversal like '../../etc/passwd' before any root joining; on Windows both '..' and '..'-style backslash forms are caught by the segment check.

Source

Thrown at studio/backend/utils/paths/storage_roots.py:413

    *,
    root: Path,
    strip_prefixes: tuple[str, ...] = (),
) -> Path:
    """Resolve ``path_value`` and assert the result is under ``root``.

    Absolutes are accepted only if already contained (so pre-resolved
    internal paths re-enter idempotently); schemas reject absolutes upstream.
    """
    if not path_value or not str(path_value).strip():
        return root

    raw = str(path_value).strip()
    if "\x00" in raw:
        raise ValueError("path may not contain null bytes")

    path = Path(raw).expanduser()
    if _has_parent_segment(raw, path):
        raise ValueError(f"path may not contain '..' segments: {raw!r}")

    if _is_absolute_user_path(path):
        _assert_contained(path, root)
        return path

    cleaned = _clean_relative_path(raw, strip_prefixes = strip_prefixes)
    candidate = root / cleaned
    _assert_contained(candidate, root)
    return candidate


def default_run_dir_name(model_name: str) -> str:
    # Folder-safe run name for an auto-created output dir. Repo ids keep their
    # namespace (org/model -> org_model); local paths (incl. G:\dir\model)
    # collapse to their final component so an absolute source can't escape
    # outputs_root. Length-capped to stay under the filesystem name limit.
    raw = str(model_name or "").strip()
    is_path = (

View on GitHub (pinned to 203007d190)

Solutions

  1. Normalize client-side first: send a clean relative path without traversal segments
  2. If you must accept user trees, use pathlib's Path.parts to filter out '..' before calling
  3. Pass absolute paths only if they are already known to be contained (the resolver accepts contained absolutes idempotently)
  4. Catch ValueError and map it to a validation error message for the user

Example fix

# before
resolve_under_root("../../secret.txt", root=root)

# after
from pathlib import PurePosixPath
rel = PurePosixPath(user_path)
if '..' in rel.parts:
    raise HTTPException(400, 'relative paths may not contain ..')
resolve_under_root(str(rel), root=root)
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import PurePath

def has_parent_segment(path_str: str) -> bool:
    return ".." in PurePath(path_str.strip()).parts or ".." in path_str.split("/")

Type guard

def is_traversal_free_path(v: object) -> bool:
    if not isinstance(v, str):
        return False
    return ".." not in PurePath(v.strip()).parts

Try / catch

try:
    path = resolve_under_root(value, root=root)
except ValueError as exc:
    if "'..' segments" in str(exc):
        raise HTTPException(400, "relative paths may not contain ..") from exc
    raise

Prevention

When it happens

Trigger: resolve_under_root("../../etc/passwd", root=...) or "models/../.." — any '..' segment in the stripped raw string or the expandeduser path. Note the check is on segments, so a filename literally containing '..' as a whole segment ("..") triggers, while '..hidden' does not.

Common situations: Clients sending relative paths built by naive joining that walk up; security probing; user-typed paths in an export/save dialog containing '..'; reusing external relative paths (e.g. from a zip or git) that assume a different working root.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/387293534ea8947d. Report an issue: GitHub.