unslothai/unsloth · error · ValueError

Could not infer role/content keys for chat column '{chat_col

Error message

Could not infer role/content keys for chat column '{chat_column}'

What it means

ValueError from standardize_chat_format's key-inference step. It inspects the distinct keys found across turns in chat_column: one unique key is treated as content-only, exactly two are split into role/content by cardinality (the key with fewer distinct values becomes the role), and anything else — zero keys or three or more — cannot be inferred and raises. The error names the chat column so you know which column to fix.

Source

Thrown at studio/backend/utils/datasets/format_conversion.py:115

    if "from" in uniques and "value" in uniques:
        role_key = "from"
        content_key = "value"
    elif "role" in uniques and "content" in uniques:
        role_key = "role"
        content_key = "content"
    elif len(uniques.keys()) == 2:
        keys = list(uniques.keys())
        length_first = len(set(uniques[keys[0]]))
        length_second = len(set(uniques[keys[1]]))
        if length_first < length_second:
            role_key = keys[0]
            content_key = keys[1]
        else:
            role_key = keys[1]
            content_key = keys[0]
    else:
        raise ValueError(f"Could not infer role/content keys for chat column '{chat_column}'")

    # Mapping for aliases
    aliases_mapping = {}
    for x in aliases_for_system:
        aliases_mapping[x] = "system"
    for x in aliases_for_user:
        aliases_mapping[x] = "user"
    for x in aliases_for_assistant:
        aliases_mapping[x] = "assistant"

    def _standardize_dataset(examples):
        convos = examples[chat_column]
        all_convos = []
        for convo in convos:
            if not isinstance(convo, list):
                all_convos.append([])
                continue

View on GitHub (pinned to 203007d190)

Solutions

  1. Pre-clean the column so every turn has exactly the role/content (or from/value) pair; drop auxiliary keys before standardizing
  2. Filter out non-dict or empty turns: dataset.filter(lambda r: all(isinstance(t, dict) and t for t in r[chat_column]))
  3. Move extra metadata to the row level (sibling columns) instead of inside each turn dict

Example fix

# before (turns: {'from','value','weight'} -> 3 unique keys -> raises)
result = standardize_chat_format(dataset, tokenizer, ..., chat_column='conversations')

# after
dataset = dataset.map(lambda r: {'conversations': [
    {'from': t['from'], 'value': t['value']} for t in r['conversations']
]})
result = standardize_chat_format(dataset, tokenizer, ..., chat_column='conversations')
Defensive patterns

Strategy: validation

Validate before calling

def inferable_chat_keys(dataset, chat_column: str) -> bool:
    """True when turns use exactly one or two distinct keys (inference works)."""
    uniques = set()
    for row in list(dataset.select(range(min(50, len(dataset))))[chat_column]):
        for turn in row or []:
            if isinstance(turn, dict):
                uniques.update(turn.keys())
    return 1 <= len(uniques) <= 2

Type guard

def is_clean_turn(turn) -> bool:
    """Turn carries only a role/content (or from/value) pair."""
    return isinstance(turn, dict) and len(turn) == 2 and (
        {"role", "content"} <= set(turn) or {"from", "value"} <= set(turn)
    )

Prevention

When it happens

Trigger: Turns carrying extra keys beyond the role/content pair, e.g. {'from','value','weight'}, {'role','content','tool_calls'}, or function-call datasets with {'from','value','function_call'}; also empty conversations or turns that are not dicts so no keys are collected.

Common situations: DPO/ORPO datasets with a 'weight' or 'score' per turn; agent/tool-use datasets with extra fields; a messages column containing None or string rows after a bad merge — all defeat the two-key inference heuristic.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/23f38ce1772df485. Report an issue: GitHub.