{"record":{"id":"ab73ec63fb3b7bc0","repo":"oobabooga/textgen","slug":"dataset-row-must-contain-messages-or-conversati","errorCode":null,"errorMessage":"Dataset row must contain \"messages\" or \"conversations\" key. Found: {list(data_point.keys())}","messagePattern":"Dataset row must contain \"messages\" or \"conversations\" key\\. Found: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"modules/training.py","lineNumber":343,"sourceCode":"        target_mods = [f\"{name}_proj\" for name, enabled in {\n            \"q\": q_proj_en, \"k\": k_proj_en, \"v\": v_proj_en, \"o\": o_proj_en,\n            \"gate\": gate_proj_en, \"down\": down_proj_en, \"up\": up_proj_en,\n        }.items() if enabled]\n        return target_mods\n\n    def normalize_messages(data_point):\n        \"\"\"Convert a dataset row to OpenAI messages format for apply_chat_template().\"\"\"\n        if \"messages\" in data_point:\n            return data_point[\"messages\"]\n\n        if \"conversations\" in data_point:\n            role_map = {\"human\": \"user\", \"gpt\": \"assistant\"}\n            return [\n                {\"role\": role_map.get(turn.get(\"from\", \"\"), turn.get(\"from\", \"\")), \"content\": turn[\"value\"]}\n                for turn in data_point[\"conversations\"]\n            ]\n\n        raise RuntimeError(\n            f'Dataset row must contain \"messages\" or \"conversations\" key. '\n            f'Found: {list(data_point.keys())}'\n        )\n\n    def tokenize_conversation(data_point):\n        \"\"\"Tokenize using apply_chat_template() with assistant-only label masking.\"\"\"\n        messages = normalize_messages(data_point)\n        full_ids = list(shared.tokenizer.apply_chat_template(messages, tokenize=True, return_dict=False))\n\n        # Build labels: -100 for everything, then unmask assistant turns.\n        # This assumes apply_chat_template(messages[:i]) is a token-for-token\n        # prefix of apply_chat_template(messages[:i+1]), which holds for all\n        # standard chat templates (Llama, ChatML, Mistral, etc.).\n        labels = [-100] * len(full_ids)\n        for i, msg in enumerate(messages):\n            if msg[\"role\"] == \"assistant\":\n                # Tokens up to where this assistant turn starts\n                header_ids = shared.tokenizer.apply_chat_template(","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/oobabooga/textgen/blob/ed888c71f221df552750e1834b3654abab8ae345/modules/training.py#L325-L361","documentation":"Thrown by normalize_messages() during LoRA/dataset training when a dataset row contains neither a 'messages' key (OpenAI chat format) nor a 'conversations' key (ShareGPT format). The training tokenizer path requires one of these schemas to build chat-template input, so any other column layout aborts preprocessing. The message lists the actual keys found, which tells you exactly what schema the row does have.","triggerScenarios":"Loading a Hugging Face dataset whose rows use a different schema (e.g. only 'text', 'prompt'/'response', 'instruction'/'input'/'output' Alpaca-style columns) and starting training with any apply_chat_template-based dataset path. Also triggered by datasets that nest the chat under another column name, or by a mapping step that was skipped.","commonSituations":"User points --dataset at an Alpaca-format JSON or a raw completion corpus while the trainer expects chat-format data; dataset was regenerated with renamed columns; a ShareGPT export uses 'from'/'value' but the top-level key is 'chat' or 'history' instead of 'conversations'.","solutions":["Convert the dataset to one of the accepted schemas: either [{'role': 'user'|'assistant'|'system', 'content': str}] under a 'messages' key, or [{'from': 'human'|'gpt', 'value': str}] under a 'conversations' key.","If the source is Alpaca-style, map instruction/output columns to messages programmatically before training (e.g. df.apply to build the messages list).","Check for typos or nested structures in the dataset column names by printing dataset[0].keys() and comparing against the two accepted keys.","As a last resort, pre-transform the dataset file offline (jq/pandas) so every row literally has 'messages' or 'conversations' at the top level."],"exampleFix":"# before (row only has Alpaca columns)\n# {\"instruction\": \"Hi\", \"output\": \"Hello!\"}  -> RuntimeError\n\n# after: convert to messages format\nimport json\nrows = [json.loads(l) for l in open('data.jsonl')]\nwith open('data_fixed.jsonl', 'w') as f:\n    for r in rows:\n        f.write(json.dumps({\"messages\": [\n            {\"role\": \"user\", \"content\": r[\"instruction\"]},\n            {\"role\": \"assistant\", \"content\": r[\"output\"]},\n        ]}) + '\\n')","handlingStrategy":"validation","validationCode":"REQUIRED_KEYS = {\"messages\", \"conversations\"}\n\ndef row_is_trainable(row: dict) -> bool:\n    return bool(REQUIRED_KEYS & set(row.keys()))\n\n# before mapping the dataset:\nbad = [i for i, row in enumerate(dataset) if not row_is_trainable(row)]\nif bad:\n    raise ValueError(f'{len(bad)} rows lack messages/conversations; first bad index: {bad[0]}, '\n                     f'keys: {list(dataset[bad[0]].keys())}')","typeGuard":"from typing import TypedDict\n\nclass Message(TypedDict):\n    role: str\n    content: str\n\nclass MessagesRow(TypedDict):\n    messages: list[Message]\n\nclass Turn(TypedDict):\n    from_: str  # 'human' | 'gpt'\n    value: str\n\nclass ConversationsRow(TypedDict):\n    conversations: list[Turn]\n\ndef is_chat_row(row: dict) -> bool:\n    if \"messages\" in row:\n        msgs = row[\"messages\"]\n        return isinstance(msgs, list) and all(\n            isinstance(m, dict) and m.get(\"role\") in {\"user\", \"assistant\", \"system\"} and isinstance(m.get(\"content\"), str)\n            for m in msgs\n        )\n    if \"conversations\" in row:\n        turns = row[\"conversations\"]\n        return isinstance(turns, list) and all(\n            isinstance(t, dict) and t.get(\"from\") in {\"human\", \"gpt\", \"system\"} and isinstance(t.get(\"value\"), str)\n            for t in turns\n        )\n    return False","tryCatchPattern":null,"preventionTips":["Standardize every training corpus to the 'messages' (OpenAI) schema before loading; it is the least ambiguous.","Add a pre-training smoke test that asserts dataset[0] contains an accepted chat key.","Keep dataset transformation code next to the dataset (a small convert script per source format) instead of ad-hoc edits."],"tags":["training","dataset","data-format","lora"],"backgroundTag":null,"analyzedSha":"ed888c71f221df552750e1834b3654abab8ae345","analyzedAt":"2026-08-15T05:24:21.000Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}