{"record":{"id":"e18e0ad0378f5caf","repo":"hiyouga/LlamaFactory","slug":"input-must-be-string-set-str-or-dict-str-str-e18e0a","errorCode":null,"errorMessage":"Input must be string, set[str] or dict[str, str], got {type(elem)}","messagePattern":"Input must be string, set\\[str\\] or dict\\[str, str\\], got (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/llamafactory/data/template.py","lineNumber":128,"sourceCode":"        r\"\"\"Get the token ids of thought words.\"\"\"\n        return tokenizer.encode(self.add_thought(), add_special_tokens=False)\n\n    def _convert_elements_to_ids(self, tokenizer: \"PreTrainedTokenizer\", elements: \"SLOTS\") -> list[int]:\n        r\"\"\"Convert elements to token ids.\"\"\"\n        token_ids = []\n        for elem in elements:\n            if isinstance(elem, str):\n                if len(elem) != 0:\n                    token_ids += tokenizer.encode(elem, add_special_tokens=False)\n            elif isinstance(elem, dict):\n                token_ids += [tokenizer.convert_tokens_to_ids(elem.get(\"token\"))]\n            elif isinstance(elem, set):\n                if \"bos_token\" in elem and tokenizer.bos_token_id is not None:\n                    token_ids += [tokenizer.bos_token_id]\n                elif \"eos_token\" in elem and tokenizer.eos_token_id is not None:\n                    token_ids += [tokenizer.eos_token_id]\n            else:\n                raise ValueError(f\"Input must be string, set[str] or dict[str, str], got {type(elem)}\")\n\n        return token_ids\n\n    def _encode(\n        self,\n        tokenizer: \"PreTrainedTokenizer\",\n        messages: list[dict[str, str]],\n        system: Optional[str],\n        tools: Optional[str],\n    ) -> list[list[int]]:\n        r\"\"\"Encode formatted inputs to pairs of token ids.\n\n        Turn 0: prefix + system + query        resp\n        Turn t: query                          resp.\n        \"\"\"\n        system = system or self.default_system\n        encoded_messages = []\n        for i, message in enumerate(messages):","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/hiyouga/LlamaFactory/blob/f28afaf6355af515454dfb16c97d728307c93897/src/llamafactory/data/template.py#L110-L146","documentation":"Template slot encoding only accepts three element types: plain strings (encoded), dicts with a 'token' key (converted via convert_tokens_to_ids), and sets containing 'bos_token'/'eos_token' (mapped to the tokenizer's special-token ids). Any other type in a formatter's slots list raises this error. It fires inside Template._convert_elements_to_ids while encoding messages.","triggerScenarios":"Registering a custom template via register_template whose formatter slots contain an int, None, list, or a dict missing the 'token' key (e.g. slots=[\"<user>\", 123, \"{{content}}\"]); passing malformed slot data when constructing StringFormatter/EmptyFormatter; subclassing Template and returning non-slot types from a custom formatter's apply().","commonSituations":"Writing a custom template for a new model family and putting a raw integer token id or None in slots instead of {\"token\": \"...\"}; copy-pasting template definitions between LlamaFactory versions where the slot schema changed; passing a dict like {\"text\": \"...\"} instead of the expected {\"token\": \"...\"}.","solutions":["Change any non-string slot to a supported form: use a string literal for text, {\"token\": \"<your_token>\"} for a token lookup, or {\"eos_token\"}/{\"bos_token\"} (set literal) for special tokens.","Inspect your register_template call and print each formatter's .slots to find the offending element type shown in the message.","If you need a raw token id, wrap it as a token string that the tokenizer knows (ensure it exists in the vocab) and use the dict form."],"exampleFix":"# before\nregister_template(\n    name=\"custom\",\n    format_user=StringFormatter(slots=[\"<user>\", 123, \"{{content}}<eos>\"]),\n)\n\n# after\nregister_template(\n    name=\"custom\",\n    format_user=StringFormatter(slots=[\"<user>{{content}}\", {\"token\": \"<eos>\"}]),\n)","handlingStrategy":"type-guard","validationCode":"from llamafactory.data.template import SLOTS\n\ndef slots_are_valid(slots) -> bool:\n    for s in slots:\n        if not isinstance(s, (str, set, dict)):\n            return False\n        if isinstance(s, set) and not s <= {\"bos_token\", \"eos_token\"}:\n            return False\n        if isinstance(s, dict) and \"token\" not in s:\n            return False\n    return True","typeGuard":"def is_valid_slot(elem) -> bool:\n    \"\"\"True if elem is accepted by Template._convert_elements_to_ids.\"\"\"\n    if isinstance(elem, str):\n        return True\n    if isinstance(elem, set):\n        return elem <= {\"bos_token\", \"eos_token\"}\n    if isinstance(elem, dict):\n        return \"token\" in elem\n    return False","tryCatchPattern":null,"preventionTips":["Only use str, {'token': ...} dicts, and {'eos_token'}/{'bos_token'} sets in formatter slots.","Unit-test custom templates by encoding one toy conversation before starting a training run."],"tags":["template","custom-template","data"],"backgroundTag":null,"analyzedSha":"f28afaf6355af515454dfb16c97d728307c93897","analyzedAt":"2026-08-14T21:57:28.298Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}