unslothai/unsloth · error · ValueError

dataset_streaming requires a plain split name in {field_name

Error message

dataset_streaming requires a plain split name in {field_name} (got {split_val!r}); use a name such as 'train' or 'validation'.

What it means

Pydantic model validator on TrainingStartRequest that runs when dataset_streaming is true. Streaming mode in HuggingFace load_dataset does not accept slice syntax (e.g. 'train[:100]' or 'train+test'), so the request is rejected early with a clear message instead of failing later inside the datasets library with 'ValueError: Bad split'. Only plain split names such as 'train' or 'validation' are accepted for train_split and eval_split.

Source

Thrown at studio/backend/models/training.py:593

    )

    @field_validator("target_modules", mode = "before")
    @classmethod
    def _normalize_target_modules(cls, value: Any) -> Any:
        # Sanitized non-LoRA history stores the unused value as null; treat it as an omitted list.
        return [] if value is None else value

    @model_validator(mode = "after")
    def _validate_streaming_splits(self) -> "TrainingStartRequest":
        # Streaming load_dataset does not accept HF slice syntax (probe-confirmed: ValueError: Bad
        # split). Reject early with a clear message so the user knows to use a plain split name.
        if self.dataset_streaming:
            for field_name, split_val in (
                ("train_split", self.train_split),
                ("eval_split", self.eval_split),
            ):
                if split_val is not None and not valid_hf_dataset_split_name(split_val):
                    raise ValueError(
                        f"dataset_streaming requires a plain split name in {field_name} "
                        f"(got {split_val!r}); use a name such as 'train' or 'validation'."
                    )
        return self

    @model_validator(mode = "after")
    def _check_steps_or_epochs(self) -> "TrainingStartRequest":
        # Each accepts 0 as "use the other"; both 0 means nothing to train.
        if (self.max_steps is None or self.max_steps == 0) and self.num_epochs == 0:
            raise ValueError("Either num_epochs or max_steps must be > 0; both cannot be 0.")
        return self

    @model_validator(mode = "after")
    def _validate_lora_variant_flags(self) -> "TrainingStartRequest":
        # The frontend only ever sends one of these and never under Full Finetuning, but a direct
        # API/YAML/CLI caller can bypass that. Nothing downstream breaks, but reject early for a
        # clear error instead of a silently-ignored flag.
        active = [

View on GitHub (pinned to 203007d190)

Solutions

  1. Set train_split/eval_split to plain split names like 'train' and 'validation' when dataset_streaming is true.
  2. If you need a subset, disable streaming and keep the slice syntax, since non-streaming load_dataset accepts it.
  3. If a smaller streamed dataset is required, take only the first N examples in the training loop (e.g. itertools.islice over the iterable dataset) instead of encoding it in the split name.

Example fix

# before
req = TrainingStartRequest(dataset_streaming=True, train_split="train[:100]", ...)

# after
req = TrainingStartRequest(dataset_streaming=True, train_split="train", ...)
Defensive patterns

Strategy: validation

Validate before calling

import re

def is_plain_split(name: str) -> bool:
    # HF split names are alphanumeric/underscore/dash; slice syntax adds [ ] + % :
    return bool(re.fullmatch(r"[A-Za-z0-9_-]+", name))

def check_streaming_splits(payload: dict) -> list[str]:
    errs = []
    if payload.get("dataset_streaming"):
        for f in ("train_split", "eval_split"):
            v = payload.get(f)
            if v is not None and not is_plain_split(v):
                errs.append(f"{f}={v!r} is not a plain split name")
    return errs

Prevention

When it happens

Trigger: POSTing a TrainingStartRequest with dataset_streaming=true and train_split='train[:500]' or eval_split='train+validation'; also any slice/percentage syntax ('train[:10%]') or a combined-split expression. Trigger occurs at Pydantic validation time, before any dataset loading starts.

Common situations: Copying a split value from a non-streaming config where slicing worked fine; downsampling large datasets via slice syntax when switching to streaming; scripts that programmatically build split names with [:N] suffixes.

Related errors


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