{"record":{"id":"2c98d4caa475a9ca","repo":"unslothai/unsloth","slug":"dataset-streaming-requires-a-plain-split-name-in","errorCode":null,"errorMessage":"dataset_streaming requires a plain split name in {field_name} (got {split_val!r}); use a name such as 'train' or 'validation'.","messagePattern":"dataset_streaming requires a plain split name in (.+?) \\(got (.+?)\\); use a name such as 'train' or 'validation'\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"studio/backend/models/training.py","lineNumber":593,"sourceCode":"    )\n\n    @field_validator(\"target_modules\", mode = \"before\")\n    @classmethod\n    def _normalize_target_modules(cls, value: Any) -> Any:\n        # Sanitized non-LoRA history stores the unused value as null; treat it as an omitted list.\n        return [] if value is None else value\n\n    @model_validator(mode = \"after\")\n    def _validate_streaming_splits(self) -> \"TrainingStartRequest\":\n        # Streaming load_dataset does not accept HF slice syntax (probe-confirmed: ValueError: Bad\n        # split). Reject early with a clear message so the user knows to use a plain split name.\n        if self.dataset_streaming:\n            for field_name, split_val in (\n                (\"train_split\", self.train_split),\n                (\"eval_split\", self.eval_split),\n            ):\n                if split_val is not None and not valid_hf_dataset_split_name(split_val):\n                    raise ValueError(\n                        f\"dataset_streaming requires a plain split name in {field_name} \"\n                        f\"(got {split_val!r}); use a name such as 'train' or 'validation'.\"\n                    )\n        return self\n\n    @model_validator(mode = \"after\")\n    def _check_steps_or_epochs(self) -> \"TrainingStartRequest\":\n        # Each accepts 0 as \"use the other\"; both 0 means nothing to train.\n        if (self.max_steps is None or self.max_steps == 0) and self.num_epochs == 0:\n            raise ValueError(\"Either num_epochs or max_steps must be > 0; both cannot be 0.\")\n        return self\n\n    @model_validator(mode = \"after\")\n    def _validate_lora_variant_flags(self) -> \"TrainingStartRequest\":\n        # The frontend only ever sends one of these and never under Full Finetuning, but a direct\n        # API/YAML/CLI caller can bypass that. Nothing downstream breaks, but reject early for a\n        # clear error instead of a silently-ignored flag.\n        active = [","sourceCodeStart":575,"sourceCodeEnd":611,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/models/training.py#L575-L611","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set train_split/eval_split to plain split names like 'train' and 'validation' when dataset_streaming is true.","If you need a subset, disable streaming and keep the slice syntax, since non-streaming load_dataset accepts it.","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."],"exampleFix":"# before\nreq = TrainingStartRequest(dataset_streaming=True, train_split=\"train[:100]\", ...)\n\n# after\nreq = TrainingStartRequest(dataset_streaming=True, train_split=\"train\", ...)","handlingStrategy":"validation","validationCode":"import re\n\ndef is_plain_split(name: str) -> bool:\n    # HF split names are alphanumeric/underscore/dash; slice syntax adds [ ] + % :\n    return bool(re.fullmatch(r\"[A-Za-z0-9_-]+\", name))\n\ndef check_streaming_splits(payload: dict) -> list[str]:\n    errs = []\n    if payload.get(\"dataset_streaming\"):\n        for f in (\"train_split\", \"eval_split\"):\n            v = payload.get(f)\n            if v is not None and not is_plain_split(v):\n                errs.append(f\"{f}={v!r} is not a plain split name\")\n    return errs","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep split names free of '[', ']', '+', '%' whenever dataset_streaming is true.","Centralize split-name construction in one helper so streaming and non-streaming paths cannot diverge.","Add a client-side unit test asserting streaming payloads only contain plain names."],"tags":["pydantic","huggingface","datasets","streaming","validation"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}