unslothai/unsloth · error · ValueError
base_precision={base_precision!r} needs a dense base repo, b
Error message
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. What it means
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.
Source
Thrown at studio/backend/core/training/diffusion_train_common.py:1108
except (TypeError, ValueError) as exc:
raise ValueError(f"ema_decay must be a number, got {self.ema_decay!r}") from exc
# 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).
if not 0.0 <= ema_decay < 1.0:
raise ValueError("ema_decay must be in [0, 1); 0 disables the EMA adapter")
# A blank cond_cache_dir (the Studio default when unset) means "off", not cwd.
cond_cache_dir = (
str(self.cond_cache_dir).strip() if self.cond_cache_dir is not None else ""
) or None
compile_transformer = str(self.compile_transformer or "auto").strip().lower()
if compile_transformer not in ("off", "on", "auto"):
raise ValueError("compile_transformer must be one of off / on / auto")
base_precision = str(self.base_precision or "nf4").strip().lower()
if base_precision not in ("nf4", "bf16", "int8", "fp8", "mxfp8", "auto"):
raise ValueError("base_precision must be one of nf4 / bf16 / int8 / fp8 / mxfp8 / auto")
# 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.
if resolved_family != "sdxl" and base_precision in ("bf16", "int8", "fp8", "mxfp8"):
if repo_is_prequantized(self.base_model):
raise ValueError(
f"base_precision={base_precision!r} needs a dense base repo, but "
f"'{self.base_model}' is already bitsandbytes-quantized. Pick the "
f"family's dense (bf16) base repo for this mode, or use nf4/auto."
)
if self.mixed_precision != "bf16":
raise ValueError(
f"base_precision={base_precision!r} trains in bf16 compute; set "
f"mixed_precision to bf16."
)
# Refuse a scheme this family's DiT is known to corrupt, and also one the training bar holds back while
# inference allows it: qwen-image fp8 now renders inside the accuracy gate, but no one has measured whether a
# LoRA converges against fp8-frozen linears, so it fails fast here rather than silently training on faith.
# MiniMax-H3 runs all three modalities through one set of linears, so the
# per-family activation range the fp8 module filter was measured against does not
# describe it. Refuse the float8 modes rather than train against a clipped forward.
if resolved_family == "minimax-h3" and base_precision in ("fp8", "mxfp8"):
raise ValueError(
f"base_precision={base_precision!r} is not supported for minimax-h3: its "View on GitHub (pinned to 203007d190)
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.
Example fix
# before
config = TrainConfig(
base_model='family/base-nf4', # pre-quantized repo
base_precision='bf16',
)
# after
config = TrainConfig(
base_model='family/base', # dense bf16 repo
base_precision='bf16',
) Defensive patterns
Strategy: validation
Validate before calling
DENSE_MODES = {"bf16", "int8", "fp8", "mxfp8"}
def check_base_model_precision_pair(base_model, base_precision, family) -> None:
if family != "sdxl" and base_precision in DENSE_MODES:
if repo_is_prequantized(base_model): # or your own list of quantized repo ids
raise ValueError(
f"base_precision={base_precision!r} needs a dense base repo; "
f"{base_model!r} is pre-quantized. Use the dense repo, or nf4/auto."
) Type guard
def is_dense_repo(repo_id) -> bool:
return not repo_is_prequantized(repo_id) Try / catch
try:
session.submit_training(config)
except ValueError as e:
if "needs a dense base repo" in str(e):
# Two valid repairs — pick deliberately, don't alternate blindly:
# config.base_model = DENSE_REPO_FOR_FAMILY[family] # keep bf16 quality
config.base_precision = "auto" # keep the quantized repo
session.submit_training(config)
else:
raise Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- base_precision must be one of nf4 / bf16 / int8 / fp8 / mxfp
- gradient_accumulation_steps must be >= 1
- lora_rank must be >= 1
- lora_alpha must be >= 1 (a zero/negative alpha scales the ad
- resolution must be a multiple of 8 and >= 64
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/d0fcd96969b5ad69.
Report an issue: GitHub.