{"record":{"id":"d0fcd96969b5ad69","repo":"unslothai/unsloth","slug":"base-precision-base-precision-r-needs-a-dense-ba","errorCode":null,"errorMessage":"base_precision={base_precision!r} needs a dense base repo, but '{self.base_model}' is already bitsandbytes-quantized. Pick the family's dense (bf16) base repo for this mode, or use nf4/auto.","messagePattern":"base_precision=(.+?) needs a dense base repo, but '(.+?)' is already bitsandbytes-quantized\\. Pick the family's dense \\(bf16\\) base repo for this mode, or use nf4/auto\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"studio/backend/core/training/diffusion_train_common.py","lineNumber":1108,"sourceCode":"        except (TypeError, ValueError) as exc:\n            raise ValueError(f\"ema_decay must be a number, got {self.ema_decay!r}\") from exc\n        # decay = 1.0 would freeze the shadow at its init forever; the update is shadow * decay + param * (1 - decay), so valid decays live in [0, 1).\n        if not 0.0 <= ema_decay < 1.0:\n            raise ValueError(\"ema_decay must be in [0, 1); 0 disables the EMA adapter\")\n        # A blank cond_cache_dir (the Studio default when unset) means \"off\", not cwd.\n        cond_cache_dir = (\n            str(self.cond_cache_dir).strip() if self.cond_cache_dir is not None else \"\"\n        ) or None\n        compile_transformer = str(self.compile_transformer or \"auto\").strip().lower()\n        if compile_transformer not in (\"off\", \"on\", \"auto\"):\n            raise ValueError(\"compile_transformer must be one of off / on / auto\")\n        base_precision = str(self.base_precision or \"nf4\").strip().lower()\n        if base_precision not in (\"nf4\", \"bf16\", \"int8\", \"fp8\", \"mxfp8\", \"auto\"):\n            raise ValueError(\"base_precision must be one of nf4 / bf16 / int8 / fp8 / mxfp8 / auto\")\n        # base_precision is a DiT-only lever, so the dense-mode gates apply only to the DiT families. The mode-name check above still runs for every family.\n        if resolved_family != \"sdxl\" and base_precision in (\"bf16\", \"int8\", \"fp8\", \"mxfp8\"):\n            if repo_is_prequantized(self.base_model):\n                raise ValueError(\n                    f\"base_precision={base_precision!r} needs a dense base repo, but \"\n                    f\"'{self.base_model}' is already bitsandbytes-quantized. Pick the \"\n                    f\"family's dense (bf16) base repo for this mode, or use nf4/auto.\"\n                )\n            if self.mixed_precision != \"bf16\":\n                raise ValueError(\n                    f\"base_precision={base_precision!r} trains in bf16 compute; set \"\n                    f\"mixed_precision to bf16.\"\n                )\n            # Refuse a scheme this family's DiT is known to corrupt, and also one the training bar holds back while\n            # inference allows it: qwen-image fp8 now renders inside the accuracy gate, but no one has measured whether a\n            # LoRA converges against fp8-frozen linears, so it fails fast here rather than silently training on faith.\n            # MiniMax-H3 runs all three modalities through one set of linears, so the\n            # per-family activation range the fp8 module filter was measured against does not\n            # describe it. Refuse the float8 modes rather than train against a clipped forward.\n            if resolved_family == \"minimax-h3\" and base_precision in (\"fp8\", \"mxfp8\"):\n                raise ValueError(\n                    f\"base_precision={base_precision!r} is not supported for minimax-h3: its \"","sourceCodeStart":1090,"sourceCodeEnd":1126,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/training/diffusion_train_common.py#L1090-L1126","documentation":"Raised when a dense base_precision mode (bf16/int8/fp8/mxfp8) is requested for a non-sdxl family while the chosen base_model repo is already bitsandbytes-quantized (detected via repo_is_prequantized). Dense modes load full-precision weights and quantize at load time; a pre-quantized repo has already-lost precision, so combining them is contradictory. The message tells you to pick the family's dense bf16 base repo, or fall back to nf4/auto.","triggerScenarios":"Setting base_precision='bf16' (or int8/fp8/mxfp8) while base_model points at a repo whose weights are already NF4/INT8 bnb-quantized — common because families ship both a dense and a quantized default repo, and the quantized one is often the inference default. sdxl is exempt (the gate applies only to non-sdxl families).","commonSituations":"Copying the inference-time default repo id (the small quantized variant) into a training config; switching base_precision from nf4 to bf16 for quality without also switching the repo id; configs generated from a model dropdown that lists quantized repos first.","solutions":["Switch base_model to the family's dense (bf16) repo id and keep base_precision='bf16'.","Or keep the quantized repo and use base_precision='nf4' or 'auto'.","Check the repo's model index/config for bnb quantization markers (bitsandbytes dtype in the weight files) when unsure which variant you have."],"exampleFix":"# before\nconfig = TrainConfig(\n    base_model='family/base-nf4',   # pre-quantized repo\n    base_precision='bf16',\n)\n\n# after\nconfig = TrainConfig(\n    base_model='family/base',        # dense bf16 repo\n    base_precision='bf16',\n)","handlingStrategy":"validation","validationCode":"DENSE_MODES = {\"bf16\", \"int8\", \"fp8\", \"mxfp8\"}\n\ndef check_base_model_precision_pair(base_model, base_precision, family) -> None:\n    if family != \"sdxl\" and base_precision in DENSE_MODES:\n        if repo_is_prequantized(base_model):  # or your own list of quantized repo ids\n            raise ValueError(\n                f\"base_precision={base_precision!r} needs a dense base repo; \"\n                f\"{base_model!r} is pre-quantized. Use the dense repo, or nf4/auto.\"\n            )","typeGuard":"def is_dense_repo(repo_id) -> bool:\n    return not repo_is_prequantized(repo_id)","tryCatchPattern":"try:\n    session.submit_training(config)\nexcept ValueError as e:\n    if \"needs a dense base repo\" in str(e):\n        # Two valid repairs — pick deliberately, don't alternate blindly:\n        #   config.base_model = DENSE_REPO_FOR_FAMILY[family]   # keep bf16 quality\n        config.base_precision = \"auto\"                          # keep the quantized repo\n        session.submit_training(config)\n    else:\n        raise","preventionTips":["Maintain a family -> dense repo id map and use it whenever base_precision is a dense mode.","Do not copy the inference default (usually the quantized repo) into training configs unthinkingly.","Keep repo id and base_precision as one validated pair in config schemas, not two independent fields."],"tags":["training","quantization","base-model","configuration","validation"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}