unslothai/unsloth · error · ValueError

path may not contain null bytes

Error message

path may not contain null bytes

What it means

Raised by resolve_under_root in storage_roots.py:409-410 when the user-supplied path string contains a NUL byte (\x00). NUL cannot appear in valid filesystem paths on POSIX or Windows and is a common marker of injection attempts or corrupted binary data, so it is rejected before any Path construction.

Source

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


def resolve_under_root(
    path_value: str | None,
    *,
    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)

View on GitHub (pinned to 203007d190)

Solutions

  1. Reject or sanitize NUL bytes at the API boundary before paths reach the resolver
  2. If binary-sourced names are legitimate, strip \x00 and re-validate the result as a safe filename
  3. Investigate where the NUL entered — usually a decode bug upstream
  4. Catch ValueError and return a 400 to the client with a clear message

Example fix

# before
name = binary_blob.decode('utf-8', errors='ignore')  # may contain \x00
resolve_under_root(name, root=root)

# after
name = binary_blob.decode('utf-8', errors='strict').replace('\x00', '')
assert name and '/' not in name
resolve_under_root(name, root=root)
Defensive patterns

Strategy: validation

Validate before calling

def path_has_no_nul(path_str) -> bool:
    return "\x00" not in (path_str or "")

Type guard

def is_nul_free_path(v: object) -> bool:
    return isinstance(v, str) and "\x00" not in v

Try / catch

try:
    path = resolve_under_root(value, root=root)
except ValueError as exc:
    if "null bytes" in str(exc):
        raise HTTPException(400, "path contains invalid characters") from exc
    raise

Prevention

When it happens

Trigger: resolve_under_root("file.bin\x00.txt") — any NUL in the raw string after strip(). Typical sources: binary data decoded as text, crafted API payloads, or truncated buffers leaking NULs into path fields.

Common situations: Fuzzing/security testing of the API; upstream components passing bytes that were decoded with errors='ignore'; copy-pasting from terminals that embed NUL; log/message data accidentally used as filenames.

Related errors


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