hiyouga/LlamaFactory · error · ValueError

Input must be string, set[str] or dict[str, str], got {type(

Error message

Input must be string, set[str] or dict[str, str], got {type(elem)}

What it means

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.

Source

Thrown at src/llamafactory/data/template.py:128

        r"""Get the token ids of thought words."""
        return tokenizer.encode(self.add_thought(), add_special_tokens=False)

    def _convert_elements_to_ids(self, tokenizer: "PreTrainedTokenizer", elements: "SLOTS") -> list[int]:
        r"""Convert elements to token ids."""
        token_ids = []
        for elem in elements:
            if isinstance(elem, str):
                if len(elem) != 0:
                    token_ids += tokenizer.encode(elem, add_special_tokens=False)
            elif isinstance(elem, dict):
                token_ids += [tokenizer.convert_tokens_to_ids(elem.get("token"))]
            elif isinstance(elem, set):
                if "bos_token" in elem and tokenizer.bos_token_id is not None:
                    token_ids += [tokenizer.bos_token_id]
                elif "eos_token" in elem and tokenizer.eos_token_id is not None:
                    token_ids += [tokenizer.eos_token_id]
            else:
                raise ValueError(f"Input must be string, set[str] or dict[str, str], got {type(elem)}")

        return token_ids

    def _encode(
        self,
        tokenizer: "PreTrainedTokenizer",
        messages: list[dict[str, str]],
        system: Optional[str],
        tools: Optional[str],
    ) -> list[list[int]]:
        r"""Encode formatted inputs to pairs of token ids.

        Turn 0: prefix + system + query        resp
        Turn t: query                          resp.
        """
        system = system or self.default_system
        encoded_messages = []
        for i, message in enumerate(messages):

View on GitHub (pinned to f28afaf635)

Solutions

  1. 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.
  2. Inspect your register_template call and print each formatter's .slots to find the offending element type shown in the message.
  3. 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.

Example fix

# before
register_template(
    name="custom",
    format_user=StringFormatter(slots=["<user>", 123, "{{content}}<eos>"]),
)

# after
register_template(
    name="custom",
    format_user=StringFormatter(slots=["<user>{{content}}", {"token": "<eos>"}]),
)
Defensive patterns

Strategy: type-guard

Validate before calling

from llamafactory.data.template import SLOTS

def slots_are_valid(slots) -> bool:
    for s in slots:
        if not isinstance(s, (str, set, dict)):
            return False
        if isinstance(s, set) and not s <= {"bos_token", "eos_token"}:
            return False
        if isinstance(s, dict) and "token" not in s:
            return False
    return True

Type guard

def is_valid_slot(elem) -> bool:
    """True if elem is accepted by Template._convert_elements_to_ids."""
    if isinstance(elem, str):
        return True
    if isinstance(elem, set):
        return elem <= {"bos_token", "eos_token"}
    if isinstance(elem, dict):
        return "token" in elem
    return False

Prevention

When it happens

Trigger: 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().

Common situations: 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": "..."}.

Related errors


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