{"record":{"id":"e9f317d78ac4561e","repo":"unslothai/unsloth","slug":"lr-scheduler-must-be-one-of-join-sorted-lr","errorCode":null,"errorMessage":"lr_scheduler must be one of {', '.join(sorted(_LR_SCHEDULERS))}; got {self.lr_scheduler!r}","messagePattern":"lr_scheduler must be one of (.+?); got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"studio/backend/core/training/diffusion_train_common.py","lineNumber":1046,"sourceCode":"        ):\n            raise ValueError(\n                f\"'{resolved_family}' trains at a resolution that is a multiple of \"\n                f\"{_VIDEO_RESOLUTION_MULTIPLE} (its VAE compresses space by that factor); \"\n                f\"got {self.resolution}.\"\n            )\n        if self.mixed_precision not in (\"bf16\", \"fp16\", \"no\"):\n            raise ValueError(\"mixed_precision must be one of bf16 / fp16 / no\")\n        # torch.manual_seed unpacks int64/uint64, so anything wider raises inside the trainer, after eviction. Catch it here.\n        if not -(2**63) <= int(self.seed) <= 2**64 - 1:\n            raise ValueError(\"seed must fit in torch's 64-bit range\")\n        # Refuse fp16 for a bf16-only DiT family up front, before evicting resident models.\n        if self.mixed_precision == \"fp16\" and resolved_family in _FORCE_BF16_FAMILIES:\n            raise ValueError(\n                f\"'{resolved_family}' LoRA training requires bf16: fp16 overflows its fp32 \"\n                f\"RoPE / embedder internals. Set mixed precision to bf16.\"\n            )\n        if str(self.lr_scheduler) not in _LR_SCHEDULERS:\n            raise ValueError(\n                f\"lr_scheduler must be one of {', '.join(sorted(_LR_SCHEDULERS))}; \"\n                f\"got {self.lr_scheduler!r}\"\n            )\n        if not 1 <= int(self.cache_variants) <= 16:\n            raise ValueError(\"cache_variants must be between 1 and 16\")\n        # Checkpointing knobs. Rejected here, before the route evicts resident GPU models, rather than deep in the loop.\n        try:\n            save_steps = int(self.save_steps or 0)\n            save_total_limit = int(self.save_total_limit or 0)\n        except (TypeError, ValueError) as exc:\n            raise ValueError(\n                f\"save_steps / save_total_limit must be whole numbers, got \"\n                f\"{self.save_steps!r} / {self.save_total_limit!r}\"\n            ) from exc\n        if save_steps < 0:\n            raise ValueError(\"save_steps must be >= 0 (0 disables periodic checkpoints)\")\n        if save_total_limit < 0:\n            raise ValueError(\"save_total_limit must be >= 0 (0 keeps every checkpoint)\")","sourceCodeStart":1028,"sourceCodeEnd":1064,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/training/diffusion_train_common.py#L1028-L1064","documentation":"The validator rejected an lr_scheduler name not present in the _LR_SCHEDULERS allowlist. The trainer maps this string to a concrete learning-rate schedule; an unknown name would otherwise KeyError or silently fall back later in the loop. The message includes the sorted list of valid names and echoes the offending value.","triggerScenarios":"Passing lr_scheduler values with wrong casing, whitespace, or different vocabulary ('CosineAnnealing', 'cosine_annealing ', 'polynomial'), or names from other training stacks (HuggingFace/DeepSpeed spellings) that do not match this trainer's set.","commonSituations":"Copy-pasting scheduler names from HuggingFace examples into this Studio's config; UI free-text input instead of a dropdown; casing/typo mistakes in YAML.","solutions":["Read the error message: it lists every valid scheduler name — use one verbatim.","Validate/normalize before submitting: str(lr_scheduler).strip() and check membership in the allowed set.","Replace UI free-text with a dropdown sourced from the same allowlist."],"exampleFix":"# before\nconfig = TrainConfig(lr_scheduler='CosineAnnealingLR')\n\n# after\nconfig = TrainConfig(lr_scheduler='cosine')  # a name from the error's allowlist","handlingStrategy":"validation","validationCode":"LR_SCHEDULERS = {\"constant\", \"cosine\", \"linear\", ...}  # mirror the trainer's allowlist\n\ndef check_lr_scheduler(v) -> str:\n    s = str(v or \"constant\").strip()\n    if s not in LR_SCHEDULERS:\n        raise ValueError(f\"lr_scheduler must be one of {sorted(LR_SCHEDULERS)}, got {v!r}\")\n    return s","typeGuard":"def is_valid_lr_scheduler(v) -> bool:\n    return str(v or \"constant\").strip() in LR_SCHEDULERS","tryCatchPattern":"try:\n    session.submit_training(config)\nexcept ValueError as e:\n    if \"lr_scheduler\" in str(e):\n        config.lr_scheduler = \"constant\"  # neutral default\n        session.submit_training(config)\n    else:\n        raise","preventionTips":["Read the error message — it enumerates the exact valid names; copy one verbatim.","Expose the allowlist as a dropdown in UIs and validate in CI config checks.","Do not paste scheduler class names from HuggingFace/PyTorch into this field."],"tags":["training","lr-scheduler","configuration","validation"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}