Settings Soup refuses or warns about at load
A soup.yaml used to be able to ask for one thing and train another: the schema accepted the setting, a validator checked its range, and for most settings nothing on the path it named ever read it (for three, listed below, what read it did the wrong thing). v0.76.0 stops that at the one place every run starts, the config load. This page is the reference for what the load does now: how it decides, the refusals grouped by what they touch with the start of each message and what to change (rows that differ only by task or backend are merged), the 29 staged fields that only warn, the deprecated values, and the keys that are still unread but not announced.
Unknown keys have refused the load since v0.75.0, and that has not changed. What is new is dozens of combinations that loaded on v0.75 and are now refused with a message that names the field and what to change. Upstream publishes no count of them, so neither does this page.
soup doctor --config soup.yamlRun that on every config that loaded on an older release before you train with v0.76.0. It loads the file with the same rules as soup train, prints the error if the load is refused, and exits 2. Unknown keys are reported together, but a refusal that spans several fields prints only the first one it hits, so fix that setting and run the command again until it prints no error.
How a config loads at v0.76.0
soup train and the other commands that read a soup.yaml (soup doctor --config, soup bench, soup cost, soup profile, soup sweep, and the --config forms of some soup eval and soup data commands) call one loader. It does these things in this order:
- The file must be a YAML mapping. An empty file prints
Config file is emptyand a list or a bare value printsConfig must be a YAML mapping, got list;soup trainexits 1 on both andsoup doctor --configexits 2. - An unknown key refuses the load. The message is
unknown config key 'x' - did you mean 'y'? Refused.When no key is close, it names the key and suggests nothing. Typos such asearly_stopp_patienceland here, and they are checked before anything else. - The schema validates the values and the combinations. A value out of range, a key that needs another key, and every refusal in the tables below come from this step. A failure prints
Config validation error:and one line per problem, each beginning with the field location. A refusal that spans several fields has an empty location, or only the section name (training:ordata:), in front of its text. - A deprecated value prints a warning and loads. The warning reads
Warning: <message> Soup v0.77 will refuse it.and is printed once per distinct message. See Deprecated values. - A staged field prints a warning and loads. The warning reads
Warning: <section>.<name> is accepted but read by nothing; v0.77 will refuse it, with one line per field. It is printed only after the config passed step 3, so a config that is refused for another reason never reaches it. See The 29 staged fields.
| Outcome | soup doctor --config | soup train |
|---|---|---|
| Refused (unknown key, a value or combination below) | prints the error, then Config could not be loaded (see above)., exits 2 | prints the error, exits 1, before it imports the training stack |
| Loads with warnings | prints each warning, exits 0 | prints each warning and trains |
| Loads clean | exits 0 | trains |
Here is a config that loads with two staged-field warnings. The keys are real, they validate, and nothing reads them:
base: org/model
task: sft
data:
train: ./data.jsonl
training:
early_stop_patience: 3
lr_groups:
- pattern: ".*lora_A.*"
lr: 0.001Warning: training.lr_groups is accepted but read by nothing; v0.77 will refuse it
training.early_stop_patience is accepted but read by nothing; v0.77 will refuse itA field is reported only when its value differs from the schema default, so early_stop_patience: 2 is silent and 3 warns. Rich wraps these lines to the width of your terminal.
What to do with a refusal. Remove the key, or change it as the message says. Deleting the key is the fix. For most rows below, v0.75 read nothing from the setting, or the run stopped after the model had loaded. Three keys are different. training.use_longlora: true, training.ebft_variant and training.quantization_aware: true WERE applied on v0.75, and wrongly, so a run that finished with one of them set did not do what its config said, and removing the key changes what trains. See Re-run this on the release page. Renaming a key does nothing useful: these settings were read or ignored, not misspelled. A config that has to load on both v0.75 and v0.76 needs the key removed.
Settings that are refused
Each row was checked against the loader and the validators at the v0.76.0 tag, and the messages were probed on the v0.76.0 binary. The third column is the exact start of the message, which always goes on to say what to change; the fourth column is that advice in short. Pull request numbers are upstream's. Where one setting is refused in several task or backend combinations the rows are merged.
Variants and modes that never ran
| Setting | Refused when | Message begins | What to change |
|---|---|---|---|
training.ebft_variant, training.ebft_temperature | ebft_variant has any value (#1230); ebft_temperature alone is refused too | training.ebft_variant is refused (#1230): EBFT is not yet a distinct objective. | Remove both keys |
training.gdpo_variant | any value (#1309) | training.gdpo_variant is refused (#1309): supported TRL versions do not expose the dpo_loss hook, so this setting was accepted but ignored. | Remove it and use plain task: dpo |
training.quantization_aware | true (#1222) | training.quantization_aware: true is refused (#1222): int8 quantization-aware training is not implemented. | Remove it. fp8 and quest take other code paths and are not refused here |
training.use_longlora | true (#1240) | training.use_longlora: true is refused (#1240): the current LongLoRA implementation leaks future tokens and is not S^2 shifted sparse attention. | Set training.rope_scaling_type (linear, dynamic, yarn or llama3) and train plain LoRA |
training.rope_scaling_type | longrope (#1239) | training.rope_scaling_type='longrope' is refused (#1239): it cannot extend any checkpoint. | Choose another rope_scaling_type |
training.kernel_auto_compose | true (#801) | kernel_auto_compose is not supported: it benchmarks the same already-loaded model for every candidate and does not apply the selected flags. | Set it to false and turn on use_liger or use_flash_attn yourself |
training.quantization | bitnet_1.58 (#825, #981) | BitNet 1.58 training is not implemented yet; | Export an existing BitNet checkpoint instead |
training.tts_family | sesame_csm (#265, #1112) | training.tts_family='sesame_csm' is not supported by Soup yet: CSM trains text plus 32 Mimi codebooks as parallel multimodal frames, | Use orpheus or llasa; spark or oute only with pre-encoded chatml data (next row); or the model's native CSM training path |
training.tts_family with data.format: audio | spark or oute (#265) | TTS family 'spark' has no installable 'sparktts' PyPI package. | Pre-encode the audio outside Soup and train with data.format: chatml; the message gives the reason for each family |
Context length and block expansion
| Setting | Refused when | Message begins | What to change |
|---|---|---|---|
training.rope_scaling_type | task is not sft, tts or pretrain; or the run is on backend: mlx or unsloth, modality: vision or audio, or stream_layers: true (#1697); the yarn_* keys set beside rope_scaling_type are named in the same message | training.rope_scaling_type is not applied by task='dpo': only task='sft', 'tts' and 'pretrain' rescale RoPE, so this run would train on the checkpoint's own RoPE. | Use one of those tasks on the text path with backend: transformers and no layer streaming, or remove the key |
training.freeze_trainable_layers | set without expand_layers (#1396) | freeze_trainable_layers only applies together with expand_layers (LLaMA Pro block expansion), where it must equal expand_layers and trains only the appended blocks. | Remove it, or set expand_layers as well |
training.freeze_trainable_layers | differs from expand_layers (#1409) | freeze_trainable_layers: 32 does not match expand_layers: 4. | Set both to the same number |
training.expand_layers | set without freeze_trainable_layers (refused before v0.76.0 as well) | expand_layers requires freeze_trainable_layers (LLaMA Pro freezes the original layers and trains only the new blocks). Set freeze_trainable_layers: 4. | Set freeze_trainable_layers to the same number as expand_layers |
training.expand_layers | quantization is not none (#1237, #1308) | training.expand_layers (LLaMA Pro block expansion) requires training.quantization: none, but it is '4bit'. | Set quantization: none |
training.expand_layers | task is not sft or pretrain, or the backend is not transformers, or the modality is not text (#1325) | training.expand_layers (LLaMA Pro) is only wired for task sft/pretrain on backend transformers with modality text; got task='dpo', backend='transformers', modality='text'. | Remove expand_layers and freeze_trainable_layers |
Adapters
| Setting | Refused when | Message begins | What to change |
|---|---|---|---|
training.loraplus_lr_ratio | combined with optimizer: apollo_adamw, lomo or adalomo (#1210) | training.loraplus_lr_ratio cannot be combined with training.optimizer='apollo_adamw': | Use an optimizer transformers can build without the model |
training.loraplus_lr_ratio | combined with use_galore: true (#1210) | training.loraplus_lr_ratio and training.use_galore are mutually exclusive: | Enable one |
training.loraplus_lr_ratio | backend: mlx (#1324) | Refused: training.loraplus_lr_ratio is not implemented on backend='mlx' | Remove it, or use backend: transformers |
training.loraplus_lr_ratio | no LoRA B matrix trains: prm, unlearn, moe_lora_routing, classifier, reranker or cross_encoder without classifier_lora, asr without asr_lora, or lora.use_vera; each message names its own reason (#745) | Refused: training.loraplus_lr_ratio needs a trainable LoRA adapter. task='prm' is a full fine-tune with no LoRA adapter, | Remove it for that task, or set training.classifier_lora: true (or training.asr_lora: true for asr) |
training.use_lorafa | combined with loraplus_lr_ratio (#725) | training.use_lorafa and training.loraplus_lr_ratio are mutually exclusive: | Enable one |
training.use_lorafa | task other than embedding, pretrain or sft; or backend is not transformers (#725) | training.use_lorafa (LoRA-FA optimizer) is currently only supported for tasks embedding, pretrain, sft; got task='dpo' | Use a supported task and backend: transformers |
training.use_lorafa | optimizer: adamw_bnb_8bit (#725) | training.use_lorafa uses an AdamW-based gradient projection and is incompatible with training.optimizer='adamw_bnb_8bit'. | Leave optimizer unset or use adamw_torch |
training.lora.use_dora, use_vera, init_strategy | a variant (dora, vera, or an init_strategy other than random) on a backend other than transformers (#1037) | training.lora variant 'dora' requires backend='transformers'; backend='unsloth' has its own adapter constructor and cannot apply this PEFT method. | Use backend: transformers or plain LoRA |
training.lora.use_dora, use_vera, init_strategy | a variant on task: moe_lora_routing (#1037) | training.lora variant 'dora' is not applied by task='moe_lora_routing': that trainer loads existing adapters with PeftModel.from_pretrained instead of constructing a new adapter. | Keep the defaults and configure the adapters through training.mole_task_adapters |
training.lora.use_dora | quantization is gptq, awq, aqlm or eetq (#1466) | training.lora.use_dora=True is not supported with training.quantization='gptq': peft cannot apply DoRA to aqlm, awq, eetq, gptq layers and raises when the adapter is attached, after the model has loaded. | Set use_dora: false, or use a 4bit, 8bit, hqq:Nbit or unquantized base |
training.lora.init_strategy | pissa or loftq with quantization other than none (#1037) | training.lora.init_strategy='pissa' requires training.quantization='none': | Set quantization: none |
training.lora | task: prm with a lora block that differs from the schema default, unless lora.r: 0 (#795) | task='prm' does not apply training.lora: the PRM trainer fine-tunes every base parameter. | Remove the lora block, or set lora.r: 0 |
training.relora_steps | quantization is not explicitly none (#693) | relora_steps requires quantization='none' (got '4bit'); ReLoRA restart merges into the base weight, which requires a writable float base. | Set quantization: none |
training.relora_steps | with lora.use_dora: true or stream_layers: true (#693) | relora_steps is incompatible with lora.use_dora=True: and relora_steps is incompatible with training.stream_layers=True: | Drop one of the two |
training.moe_lora | task: asr (#1151), modality: vision or audio (#1179), backend: unsloth (#1264) | training.moe_lora is not applied by task='asr': that trainer loads Whisper, which has no expert layers. | Remove it, or use the text sft path on backend: transformers |
training.moe_aux_loss_coeff | a value other than the default 0.01, on a task other than sft, tts or pretrain (#798), or on task: sft with modality: vision or audio (#1394). moe_expert_quant and train_router_only are refused on any task other than sft and tts (#798) | training.moe_aux_loss_coeff=0.05 is not applied by task='dpo': only ['pretrain', 'sft', 'tts'] read it (sft.py, pretrain.py). | Remove it, or use one of those tasks on the text path |
Optimizers
The allowlist is 24 names, down from 32. The checks apply to training.optimizer (#1269, #1283).
| Setting | Refused when | Message begins | What to change |
|---|---|---|---|
training.optimizer | adam_mini, adamw_apex_fused, ao_adamw_4bit, ao_adamw_8bit, ao_adamw_fp8, badam, came_pytorch or dion | optimizer='badam' is no longer supported: transformers 5.x rejects it in TrainingArguments after the model has loaded, so Soup refuses it at config load instead. | Use adamw_torch, adamw_torch_fused, adamw_bnb_8bit, adafactor or another name from the 24-name allowlist |
training.optimizer | muon or adamw_hf on any backend except mlx | training.optimizer='muon' only works on backend: mlx (the MLX backend builds it itself); | Switch to backend: mlx, or use a supported optimizer |
Quantization
| Setting | Refused when | Message begins | What to change |
|---|---|---|---|
training.quantization | gptq, awq or another quant-menu value on distill, classifier, reranker, cross_encoder, prm, moe_lora_routing, unlearn or asr (#795) | task='distill' does not apply training.quantization: its trainer never quantises the base, so quantization='gptq' would record a quantised run that never happens. | Remove it, or set quantization: none. 4bit and 8bit on these tasks only warn: see Deprecated values |
training.bnb_4bit_quant_storage | quantization is not 4bit (#1466) | bnb_4bit_quant_storage='uint8' requires quantization '4bit', got 'mxfp4'. | Use quantization: 4bit, or remove the key |
training.bnb_4bit_quant_storage | set on distill, classifier, reranker, cross_encoder, prm, moe_lora_routing, unlearn or asr (#795) | task='distill' does not apply training.quantization: its trainer never quantises the base, so quantization='4bit' would record a quantised run that never happens. | Remove the key, or set quantization: none |
training.quantization_aware: fp8 | backend: unsloth (#1124) | v0.28.0 features ['quantization_aware="fp8"'] are not supported on the unsloth backend (unsloth uses its own fused kernels). | Use backend: transformers, or remove the flag |
Task and backend scope
| Setting | Refused when | Message begins | What to change |
|---|---|---|---|
backend: unsloth | task is outside sft, dpo, grpo, ppo, kto, orpo, simpo, ipo, bco, preference, pretrain, embedding and tts (#1357) | backend='unsloth' is not applied by task='classifier': that trainer has no unsloth setup and would train on plain transformers. | Use backend: transformers |
backend: unsloth | task: dpo with modality: vision (#1393) | task='dpo' with modality='vision' requires backend='transformers': | Use backend: transformers |
training.use_liger, training.use_flash_attn | task is not sft or tts (#806) | training.use_liger=true requires task in ['sft', 'tts']; got task='dpo' | Remove it, or use task: sft |
training.auto_mixed_precision | task is not sft or tts (#1618) | training.auto_mixed_precision=true is not applied by task='dpo': only task in ['sft', 'tts'] picks bf16/fp16 from the model and GPU | Remove it, or set precision explicitly |
training.freeze_layers, training.freeze_ratio | task is not sft or tts (#1497), or the run is on backend: mlx or unsloth, modality: vision or audio, or stream_layers: true (#1581) | training.freeze_layers is not applied by task='dpo': only task='sft' (and 'tts', which trains through the SFT trainer) freezes layers, so this run would train every layer. | Use the text sft path on backend: transformers with no layer streaming, or remove the key |
training.batch_size | task: kto with batch_size: 1 (#1420) | task='kto' requires training.batch_size >= 2: TRL's KL term is degenerate at a per-device batch of 1. | Set batch_size to 2 or more and raise gradient_accumulation_steps |
training.batch_size | task: embedding with the default contrastive loss at batch_size: 1 (#1234) | contrastive in-batch negatives need batch_size >= 2; use triplet (with negatives) or cosine for batch 1 | Raise batch_size, or choose another embedding loss |
training.dpo_ref_regen_epochs | lora.r: 0 on task: dpo, ipo or preference (#1345); the key is itself accepted only on those tasks (preference with preference_loss dpo or ipo) | dpo_ref_regen_epochs requires LoRA (lora.r >= 1): | Use LoRA, or remove the key |
training.minillm_teacher_mix_ratio | minillm_enabled with minillm_on_policy: false and a ratio of 0 (#692) | minillm_enabled with minillm_on_policy=false requires minillm_teacher_mix_ratio > 0; | Set the ratio in (0, 1], or set minillm_on_policy |
training.distill_chunk_size | outside task: distill, with minillm_enabled, or below 1 (#722) | Distillation fields ['distill_chunk_size'] require task='distill'; got task='sft' | Use task: distill without MiniLLM, and a chunk size of 1 or more |
training.grpo_beta | a boolean (#1247) | grpo_beta must not be a boolean | Give a number; 0 runs KL-free |
training.loss_watchdog, training.loss_spike_recovery, training.grad_accum_auto_tune | true on task: prm, moe_lora_routing or unlearn (#802), or on backend: mlx (#1069) | training.loss_watchdog is not supported for task='prm' because 'prm' does not attach a live training callback | Remove it for that task or backend |
training.eval_steps | not an integer (#1223) | training.eval_steps must be an int, not a bool | Give an integer number of optimizer steps |
training.eval_steps | data.val_split is 0 and no source carries its own validation split (#1223) | training.eval_steps is set, but there is nothing to evaluate: | Set data.val_split above 0, or remove eval_steps |
training.eval_steps | backend: mlx, or task is ppo, online_dpo or unlearn (#1223) | training.eval_steps is not supported for backend='mlx' because mlx-lm evaluates the validation split on its own cadence (#1223) | Remove it |
Data
| Setting | Refused when | Message begins | What to change |
|---|---|---|---|
data.add_new_tokens, data.new_special_tokens | task is pretrain, embedding, ppo, reward_model, prm, classifier, reranker, cross_encoder, unlearn, moe_lora_routing, distill or asr; or backend is mlx or unsloth (SFT's vision and audio paths on unsloth excepted); or stream_layers: true (#1358) | data.add_new_tokens is not applied by task='pretrain': that trainer never adds tokens to its tokenizer or resizes its embeddings. | Remove it, or use backend: transformers with a task that adds tokens, such as sft or dpo |
data.add_new_tokens | with data.format: pre_tokenized | format='pre_tokenized' cannot be combined with data.add_new_tokens; soup data preprocess never applies it, so the cached ids do not contain it. | Remove it, or train from the source format |
data.format: pre_tokenized | with modality: vision | data.format='pre_tokenized' cannot be combined with modality='vision'; | Train from the source format |
data.chat_template | task is pretrain, embedding, classifier, reranker, cross_encoder, prm, asr, moe_lora_routing or unlearn (#804) | data.chat_template is not used by task='pretrain'; remove it or choose a conversational training task | Remove it |
data.mask_history | true with train_on_responses_only: false (#761) | data.mask_history requires data.train_on_responses_only: true | Set train_on_responses_only: true, or drop mask_history |
Layer streaming
The streaming checks run on training.stream_layers: true. The full streaming reference is Layer streaming and Layer streaming disk tier.
| Setting | Refused when | Message begins | What to change |
|---|---|---|---|
training.stream_source, stream_ngram_source, stream_buffers, stream_read_ahead, stream_vram_override, stream_vram_probe, stream_disk_kind, stream_pin | set while stream_layers is false | training.stream_source / training.stream_ngram_source / training.stream_buffers / training.stream_read_ahead / training.stream_vram_override / training.stream_vram_probe / training.stream_disk_kind / training.stream_pin set but stream_layers is false; | Set stream_layers: true, or remove the keys |
training.stream_read_ahead | stream_source: ram (#971) | training.stream_read_ahead=4 has no effect with training.stream_source='ram': the read-ahead reader belongs to the NVMe disk tier, and 'ram' never falls back to it. | Set stream_source: auto or disk, or drop the key |
training.stream_read_ahead | above 8 (#971) | training -> stream_read_ahead: Input should be less than or equal to 8 | Use 8 or less |
training.batch_size: auto | with stream_layers: true | training.stream_layers cannot use batch_size='auto': the probe sizes a RESIDENT model, which a streaming run never loads. | Set a concrete batch_size |
training.lora.target_modules | contains lm_head or embed_tokens under stream_layers (#1012) | training.stream_layers does not yet support a LoRA target on lm_head: | Drop lm_head or embed_tokens from target_modules |
training.use_liger, use_flash_attn, nvfp4, quantization_aware | under stream_layers (#1692, #1124) | training.stream_layers is mutually exclusive with use_liger: streaming owns the model-construction path (meta skeleton + per-layer weight substitution) | Turn off one of the two |
The 29 staged fields
These 29 fields are accepted by the schema, validated, and read by nothing. Each prints Warning: <field> is accepted but read by nothing; v0.77 will refuse it when its value differs from the default in the second column, and the load continues with exit 0. By upstream's stated deadline, v0.77 refuses them. That deadline is printed by the CLI and pinned by a test upstream (#808, #761); this site cannot date v0.77 itself. The warning is the same on every task. The last column lists the other conditions the schema enforces on the field, which can refuse the config before the warning.
| Field | Silent default | Note |
|---|---|---|
training.long_context_grpo | false | Needs task: grpo, not backend: mlx, and use_ring_attention off |
training.vision_grpo | false | Needs task: grpo or ppo, modality: vision and a known vision-language base |
training.quantize_ref_model | false | Needs a task with a reference model: dpo, grpo, ppo, kto, orpo, simpo, ipo, bco or preference |
training.grace_codebook | false | Needs grace_codebook_size and grace_codebook_dim |
training.grace_codebook_size | unset | Needs grace_codebook: true; bounds 1 to 100000 |
training.grace_codebook_dim | unset | Needs grace_codebook: true; bounds 1 to 16384 |
training.lr_groups | unset | A list of {pattern, lr}, at most 32, each pattern checked as a regular expression |
training.early_stop_patience | 2 | Bound 1 to 10 |
training.citation_recall_threshold | unset | Needs citation_faithful: true and data.format: raft |
training.forgetting_eval_steps | 100 | Bound 10 to 10000 |
training.forgetting_benchmark | mini_mmlu | One of mini_mmlu, mini_common_sense, mini_instruction |
training.forgetting_stop | false | |
training.checkpoint_eval_steps | 200 | Bound 50 to 10000 |
training.checkpoint_eval_metric | composite | One of judge, mmlu, custom, composite |
training.checkpoint_eval_tasks | unset | A path |
training.checkpoint_keep_top | 3 | Bound 1 to 20 |
training.convergence_window | 50 | Bound 5 to 10000 |
training.convergence_rel_tol | 0.005 | Above 0 and at most 1 |
data.video_dir | unset | Video fields need data.format of video, multimodal or auto |
data.video_fps | unset | Same |
data.video_maxlen | unset | Same |
data.image_min_pixels | unset | |
data.image_max_pixels | unset | |
data.image_resize_algorithm | unset | |
data.split_thinking | false | |
data.eval_on_each_dataset | false | |
data.resize_vocab | false | Needs add_new_tokens or new_special_tokens to be non-empty |
data.extend_conversation | false | |
data.skip_prepare_dataset | false |
The training-intelligence fields in this table (forgetting_*, checkpoint_eval_*, checkpoint_keep_top, convergence_*, early_stop_patience) are the tunables of features that are documented on Training intelligence and Training stability. Setting them to a non-default value changes nothing about the run, and since v0.76.0 it says so.
Deprecated values
Three values load, are ignored, and print a warning that ends Soup v0.77 will refuse it.. They are separate from the staged fields: the two config values were defaults that Soup's own tools wrote into the configs they generated, and refusing them at once would have broken those files (#759, #795), so they warn for one release first.
| Value | What the warning says | What to change |
|---|---|---|
data.remove_unused_columns: true | data.remove_unused_columns: true has no effect and is ignored (no trainer reads it; the trainers that set it pass false). Delete the line. | Delete the line. The default is now false |
An explicit training.quantization: 4bit or 8bit (or load_in_8bit: true) on distill, classifier, reranker, cross_encoder, prm, moe_lora_routing, unlearn or asr | training.quantization: 4bit has no effect on task='distill' and is ignored: its trainer never quantises the base, so the run trains unquantised. Set quantization: none. | Set quantization: none. An unset value on those tasks now resolves to none without a warning |
soup ab --beta | --beta no longer does anything (#1418): soup ab now accepts H0 once the confidence sequence for the difference at level --alpha lies inside +-effect_size, followed by Soup v0.77 will refuse it. | Remove --beta; to make accept_h0 stricter, lower --alpha |
The first two are config values and print when the config loads. The third is a command option, so soup ab prints its warning, not the loader.
AWQ and GPTQ export. soup export --format awq and soup export --format gptq still work in v0.76.0, and soup quantize --to awq and soup quantize --to gptq still print the soup export command for you to run (soup quantize quantizes nothing itself). All four print a Deprecated: line saying the format is deprecated and will be removed in Soup v0.77, that the upstream project (AutoAWQ or AutoGPTQ) is archived, that the extra cannot be installed next to [train], and that you should use gguf, onnx, tensorrt, bitnet or tq1_0. This is a command deprecation, not a config one. See Export.
Keys that are still unread and not staged
Not every key that nothing reads is in the staged table, and these do not warn at load. House phrasing for them is the same as before this release: accepted by the schema, but nothing in v0.76.0 reads it. No release is named for them, so do not read the v0.77 date above as covering them.
- Four parent flags and one threshold:
training.forgetting_detection,training.checkpoint_intelligence,training.early_stop_on_regression,training.convergence_detection(whentrue) andtraining.forgetting_threshold(when it differs from the default).soup trainprints aNote:after the load says they are set but not enforced during training;soup doctor --configdoes not print it. Use--gate <suite.yaml>for a live eval gate, or runsoup evalorsoup diagnoseafter training. training.warmup_auto. It loads in silence.soup autopilotwrites it into the config it generates, and nothing that trains reads it.training.packing_cross_doc_attn_maskis already refused at load, as before.training.load_in_16bit,training.unsloth_bnb_4bitandtraining.llm_int8are aliases that the schema rewrites, not separate settings.
Upstream tracks the whole class as #1814: a guard test pins 36 declared fields as unconsumed, which is the 29 staged fields plus warmup_auto, forgetting_threshold, remove_unused_columns, packing_cross_doc_attn_mask and the three aliases. Data pipeline pro uses the same wording for the video and image fields.
What upstream says v0.77 will refuse
By upstream's stated deadline, these stop loading in v0.77, and this release is the notice. Deleting the key now keeps the config loading after v0.77.
- the 29 staged fields, which would become load errors; the text upstream has written for that case is
<field> is accepted but read by nothing; refused as of v0.77(#808, #1106); data.remove_unused_columns: true(#759);- an explicit
training.quantization: 4bitor8biton the eight tasks that never quantize (#795); soup ab --beta(#1418);soup export --format awq|gptqandsoup quantize --to awq|gptq, which upstream says are removed (#338, #1690; removal tracked in #1710).
Upstream says more configs will stop loading in that release. soup doctor --config shows the staged fields and deprecated values now.
What soup doctor --config does and does not check
soup doctor --config soup.yaml loads the file with the loader described above. So, on every task and backend, it reports unknown keys (all of them together) and the refusals on this page that happen at config load. It does not list every refusal in one run: it stops at the first cross-field refusal it hits, so fix that setting and run it again until it prints no error. The staged-field warnings are printed only for a config that loads; a deprecated-value warning can also appear above a refusal, if its check ran before the check that refused the config. A refused config exits 2 and a clean or warning-only config exits 0.
After a successful load it prints a Config check section. That part is small. It is a table of settings that are known to be unread on a specific combination of task, backend and modality, and it has four declared entries: task: sft on backend: mlx with 18 settings, task: sft on backend: unsloth with 3, and task: sft on the transformers backend with a vision modality (2 settings) or an audio modality (2 settings). Outside those four it reports nothing. Its all-clear line reads None of the N setting(s) known to be unread on task=sft backend=transformers is switched on in this config., and for a plain text SFT run on transformers N is 0. That line says the same thing whether or not a setting is unread, so it is not an all-clear. Upstream tracks this as #1815: the declared tables cover 23 of the shipped recipes, and the largest gaps are sft, grpo and dpo on the transformers backend with text.
Two other limits. A few refusals happen later than config load, at trainer setup, and soup doctor --config cannot see them: for example task: ppo with reward_hack_detector, reward_hack_mitigation or echo_trap_enabled, which soup train refuses when it builds the trainer on trl's experimental PPOTrainer, before the model loads (task: ppo itself still runs; the refusal is for the flags PPO cannot feed). And soup train --dry-run validates the config and the data and, for a run that is not layer-streamed and has a GPU whose memory it can read, the hardware-fit check; it does not run the layer-streaming VRAM pre-flight, which runs at trainer setup. soup plan and soup apply read the YAML with the unknown-key check only (an unknown key exits 1), not with the schema validators, so soup plan --config writes a plan for training.quantization_aware: true, which soup doctor --config and soup train refuse; use soup doctor --config as the check. See MLX honours its config for the MLX table, and Exit codes for the other commands' codes.
See also
- v0.76.0: settings that never took effect now stop the load: the release page, with the breaking changes and the re-run table.
- Configuration: the reference for the config file and how it is loaded.
- Pre-flight tooling and Fine-tune Doctor: the other checks to run before a long job.
- Exit codes.
Soup is free and Apache-2.0. If it saved you a training run, starring the repo costs nothing and helps most. You can also fund the GPU time behind the work a 4 GB laptop cannot reach.