unslothai/unsloth · warning · ValueError

dataset path may not contain null bytes

Error message

dataset path may not contain null bytes

What it means

First guard in resolve_dataset_path: the raw path string contains a NUL byte (\x00). NUL cannot appear in real filesystem paths and would otherwise be smuggled into OS syscalls where it truncates or corrupts the path — a classic path-traversal/injection vector. It is rejected before any normalization.

Source

Thrown at studio/backend/hub/utils/paths.py:393

    """True when *path* is *root* or lives beneath it.

    Compares real (symlink-resolved, case-normalized) paths so the check holds
    through symlinks and on case-insensitive filesystems, where a plain
    ``Path.is_relative_to`` would miss a casing-only match. Returns False on any
    resolution error rather than raising.
    """
    try:
        path_real = os.path.normcase(os.path.realpath(str(path)))
        root_real = os.path.normcase(os.path.realpath(str(root)))
        return os.path.commonpath([path_real, root_real]) == root_real
    except (OSError, ValueError):
        return False


def resolve_dataset_path(path_value: str) -> Path:
    raw = str(path_value or "").strip()
    if "\x00" in raw:
        raise ValueError("dataset path may not contain null bytes")
    # Normalize first so Windows/UNC and backslash paths resolve like the rest of the Hub path
    # layer, and a backslashed '..' is caught by the traversal guard below.
    normalized = normalize_path(raw)
    path = Path(normalized).expanduser()
    if ".." in path.parts:
        raise ValueError(f"dataset path may not contain '..' segments: {raw!r}")
    if path.is_absolute():
        for root in (datasets_root(), dataset_uploads_root(), recipe_datasets_root()):
            try:
                _assert_contained(path, root)
                return path
            except ValueError:
                continue
        raise ValueError(f"dataset path must be relative or under a dataset root: {raw!r}")

    parts = [part for part in Path(normalized).parts if part not in ("", ".")]
    if parts[:2] == ["assets", "datasets"]:
        parts = parts[2:]

View on GitHub (pinned to 203007d190)

Solutions

  1. Strip/reject NUL bytes client-side before sending the path.
  2. Fix the producer of the string (encoding bug, buffer concatenation) — a NUL in a path is always a bug upstream.
  3. Server-side, catch ValueError and return 400 rather than letting it become a 500.

Example fix

// before
body = { path: rawBuffer.toString('utf8') }  // may contain \0
// after
const p = rawBuffer.toString('utf8');
if (p.includes('\0')) throw new Error('invalid path');
body = { path: p };
Defensive patterns

Strategy: validation

Validate before calling

def dataset_path_is_clean(path_value: str) -> bool:
    raw = (path_value or "").strip()
    return bool(raw) and "\x00" not in raw

Try / catch

try:
    p = resolve_dataset_path(value)
except ValueError as e:
    if "null bytes" in str(e):
        return bad_request("path contains invalid characters")  # 400, not 500

Prevention

When it happens

Trigger: Calling any dataset path API with a value containing \x00, e.g. 'data/\x00/../../etc' or a binary-mangled payload from a client that concatenates C strings.

Common situations: Fuzzing or malformed multipart/upload payloads; values decoded from a broken encoding; C-extension or shell glue that passes NUL-terminated buffers into Python.

Related errors


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