{"record":{"id":"60bc613b3ab67c15","repo":"hiyouga/LlamaFactory","slug":"mask-history-is-incompatible-with-train-on-prom","errorCode":null,"errorMessage":"`mask_history` is incompatible with `train_on_prompt`.","messagePattern":"`mask_history` is incompatible with `train_on_prompt`\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/llamafactory/hparams/data_args.py","lineNumber":183,"sourceCode":"        if self.interleave_probs is not None:\n            if self.mix_strategy == \"concat\":\n                raise ValueError(\"`interleave_probs` is only valid for interleaved mixing.\")\n\n            self.interleave_probs = list(map(float, split_arg(self.interleave_probs)))\n            if self.dataset is not None and len(self.dataset) != len(self.interleave_probs):\n                raise ValueError(\"The length of dataset and interleave probs should be identical.\")\n\n            if self.eval_dataset is not None and len(self.eval_dataset) != len(self.interleave_probs):\n                raise ValueError(\"The length of eval dataset and interleave probs should be identical.\")\n\n        if self.streaming and self.val_size > 1e-6 and self.val_size < 1:\n            raise ValueError(\"Streaming mode should have an integer val size.\")\n\n        if self.streaming and self.max_samples is not None:\n            raise ValueError(\"`max_samples` is incompatible with `streaming`.\")\n\n        if self.mask_history and self.train_on_prompt:\n            raise ValueError(\"`mask_history` is incompatible with `train_on_prompt`.\")\n\n        if self.neat_packing:\n            self.packing = True\n\n        if self.packing:\n            self.cutoff_len -= 1  # avoid pad_to_multiple_of, needs improve\n\n    def to_dict(self) -> dict[str, Any]:\n        return asdict(self)\n","sourceCodeStart":165,"sourceCodeEnd":193,"githubUrl":"https://github.com/hiyouga/LlamaFactory/blob/f28afaf6355af515454dfb16c97d728307c93897/src/llamafactory/hparams/data_args.py#L165-L193","documentation":"train_on_prompt keeps loss on the prompt tokens, while mask_history trains on only the last response turn by masking earlier dialogue history. Applying both is contradictory (the masked-away history includes prompt tokens), so DataArguments.__post_init__ (src/llamafactory/hparams/data_args.py:183) rejects it. Choose one behavior.","triggerScenarios":"A dataset/config with mask_history: true and train_on_prompt: true simultaneously. Fired during DataArguments validation at startup.","commonSituations":"Users fine-tuning multi-turn chat models who copy flags from different example configs (one using train_on_prompt for knowledge learning, another using mask_history for last-turn supervision) and end up with both.","solutions":["If you want loss only on the latest assistant turn, keep mask_history: true and remove train_on_prompt.","If you want loss on the entire sequence including prompts, keep train_on_prompt: true and remove mask_history.","Leave both off for the default behavior: loss on all assistant responses in multi-turn data."],"exampleFix":"# before (yaml)\nmask_history: true\ntrain_on_prompt: true\n\n# after (yaml)\nmask_history: true\n# train_on_prompt removed","handlingStrategy":"validation","validationCode":"def check_mask_flags(mask_history: bool, train_on_prompt: bool) -> None:\n    if mask_history and train_on_prompt:\n        raise ValueError(\"mask_history and train_on_prompt are mutually exclusive\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Decide supervision scope first (last turn vs all tokens), then set exactly one flag.","Document in the config file which behavior each run used, to avoid flag drift between experiments."],"tags":["config","multi-turn","loss-masking","data"],"backgroundTag":null,"analyzedSha":"f28afaf6355af515454dfb16c97d728307c93897","analyzedAt":"2026-08-14T21:57:28.298Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}