hiyouga/LlamaFactory · error · ValueError

No valid messages or chosen_messages/rejected_messages found

Error message

No valid messages or chosen_messages/rejected_messages found in sample.

What it means

The renderer's batch path looks for 'messages' (plain SFT/generation) or 'chosen_messages'/'rejected_messages' (preference pairs like DPO/KTO) in each sample. If none are present the sample has no renderable content and a ValueError is raised instead of silently emitting an empty batch.

Source

Thrown at src/llamafactory/v1/core/rendering/rendering.py:313

                # chosen and rejected are independent sequences; position ids must restart at 1 for
                # each (a single continuous range would offset rejected's positional embeddings).
                model_input["position_ids"] = list(range(1, len(chosen_input["input_ids"]) + 1)) + list(
                    range(1, len(rejected_input["input_ids"]) + 1)
                )

                for key in _MULTIMODAL_PASSTHROUGH_KEYS:
                    tensors = [inp[key] for inp in (chosen_input, rejected_input) if key in inp]
                    if tensors:
                        model_input[key] = torch.cat(tensors, dim=0)

                if "mm_token_type_ids" in chosen_input or "mm_token_type_ids" in rejected_input:
                    chosen_mm = chosen_input.get("mm_token_type_ids", [0] * len(chosen_input["input_ids"]))
                    rejected_mm = rejected_input.get("mm_token_type_ids", [0] * len(rejected_input["input_ids"]))
                    model_input["mm_token_type_ids"] = chosen_mm + rejected_mm

                rendered.append(model_input)
            else:
                raise ValueError("No valid messages or chosen_messages/rejected_messages found in sample.")

            for model_input in rendered:
                if "extra_info" in sample:
                    model_input["extra_info"] = sample["extra_info"]
                if "_dataset_name" in sample:
                    model_input["_dataset_name"] = sample["_dataset_name"]
                model_inputs.append(model_input)

        return model_inputs

View on GitHub (pinned to f28afaf635)

Solutions

  1. Rename/emit the expected key: 'messages' for standard samples, or both 'chosen_messages' and 'rejected_messages' for preference samples
  2. Filter malformed samples during preprocessing: keep only samples containing at least one of the accepted key sets
  3. If your data is pre-tokenized, bypass this renderer path rather than feeding token ids as messages

Example fix

# before
sample = {"conversation": [{"role": "user", "content": "hi"}, ...]}

# after
sample = {"messages": [{"role": "user", "content": "hi"}, ...]}
Defensive patterns

Strategy: validation

Validate before calling

def sample_is_renderable(sample: dict) -> bool:
    return "messages" in sample or ("chosen_messages" in sample and "rejected_messages" in sample)

Type guard

def is_renderable_sample(sample: dict) -> bool:
    return (
        isinstance(sample, dict)
        and ("messages" in sample
             or ("chosen_messages" in sample and "rejected_messages" in sample))
    )

Prevention

When it happens

Trigger: Passing a dataset of samples that only contain pre-tokenized fields (input_ids), raw 'conversations'/'messages' under a different key name, or a preference sample with only one of chosen/rejected; an empty dict sample.

Common situations: Custom dataset converters that keep the original column name ('conversation', 'chat', 'history') instead of the expected keys; mixed-format datasets where some rows lack the fields.

Related errors


AI-assisted analysis of hiyouga/LlamaFactory@f28afaf635 (2026-08-14). Data as JSON: /api/errors/846669722e75f5d5. Report an issue: GitHub.