{"record":{"id":"f46c746f2e973149","repo":"unslothai/unsloth","slug":"resolved-family-lora-training-requires-bf16-f","errorCode":null,"errorMessage":"'{resolved_family}' LoRA training requires bf16: fp16 overflows its fp32 RoPE / embedder internals. Set mixed precision to bf16.","messagePattern":"'(.+?)' LoRA training requires bf16: fp16 overflows its fp32 RoPE / embedder internals\\. Set mixed precision to bf16\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"studio/backend/core/training/diffusion_train_common.py","lineNumber":1041,"sourceCode":"        # A video family's VAE compresses space by 32, so an off-grid resolution changes the\n        # latent geometry silently. Refuse it here, before the GPU models are evicted.\n        if (\n            resolved_family in TRAINABLE_VIDEO_FAMILIES\n            and self.resolution % _VIDEO_RESOLUTION_MULTIPLE != 0\n        ):\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}\"","sourceCodeStart":1023,"sourceCodeEnd":1059,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/training/diffusion_train_common.py#L1023-L1059","documentation":"Raised when mixed_precision='fp16' is requested for a family in _FORCE_BF16_FAMILIES. These bf16-only DiT architectures run their RoPE positional encoding and text embedders in fp32; fp16 overflows those internals and the training diverges or NaNs. The validator refuses the combination up front, before GPU models are evicted.","triggerScenarios":"Training a bf16-only DiT family (e.g. modern DiT video/image models) with mixed_precision='fp16'. Often happens when a config that worked on an fp16-era UNet model (SD1.5/SDXL-class) is reused unchanged on a DiT family.","commonSituations":"Porting a known-good SDXL fp16 recipe to a newer DiT base model; choosing fp16 on pre-Ampere GPUs where bf16 is slow/unavailable; hyperparameter sweeps that try all precision options.","solutions":["Set mixed_precision='bf16' for this family (the error message says exactly this).","If the GPU cannot do bf16 (pre-Ampere NVIDIA), you cannot train this family in fp16 — use mixed_precision='no' (fp32) or switch hardware/base model.","Tag per-family precision defaults in your config templates so fp16 is never applied to bf16-only families."],"exampleFix":"# before\nconfig = TrainConfig(base_model='...', mixed_precision='fp16')\n\n# after\nconfig = TrainConfig(base_model='...', mixed_precision='bf16')","handlingStrategy":"validation","validationCode":"def check_family_precision(family, mixed_precision) -> str:\n    # Mirror the trainer's gate: bf16-only DiT families refuse fp16.\n    if mixed_precision == \"fp16\" and family in FORCE_BF16_FAMILIES:\n        raise ValueError(f\"{family} requires bf16; fp16 overflows its RoPE/embedder internals\")\n    return mixed_precision","typeGuard":"def supports_fp16(family) -> bool:\n    return family not in FORCE_BF16_FAMILIES","tryCatchPattern":"try:\n    session.submit_training(config)\nexcept ValueError as e:\n    if \"requires bf16\" in str(e):\n        config.mixed_precision = \"bf16\"\n        session.submit_training(config)\n    else:\n        raise","preventionTips":["Default new configs to bf16 — it is the safe precision for all current DiT families.","Keep a per-family precision capability table next to your model registry.","Before scheduling on older GPUs, verify bf16 support; if absent, these families need fp32 ('no') or different hardware."],"tags":["training","mixed-precision","bf16","dit","validation"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}