hiyouga/LlamaFactory · error · ValueError
Stop words are required to replace the EOS token.
Error message
Stop words are required to replace the EOS token.
What it means
Some templates set replace_eos=True to make the tokenizer's EOS a template-specific stop word (the first entry of stop_words). If stop_words is empty or None for such a template, there is nothing to swap in and LlamaFactory raises this ValueError in fix_special_tokens before tokenization starts.
Source
Thrown at src/llamafactory/data/template.py:194
return
is_added = tokenizer.eos_token_id is None
num_added_tokens = tokenizer.add_special_tokens({"eos_token": eos_token})
if is_added:
logger.info_rank0(f"Add eos token: {tokenizer.eos_token}.")
else:
logger.info_rank0(f"Replace eos token: {tokenizer.eos_token}.")
if num_added_tokens > 0:
logger.warning_rank0("New tokens have been added, make sure `resize_vocab` is True.")
def fix_special_tokens(self, tokenizer: "PreTrainedTokenizer") -> None:
r"""Add eos token and pad token to the tokenizer."""
stop_words = self.stop_words
if self.replace_eos:
if not stop_words:
raise ValueError("Stop words are required to replace the EOS token.")
self._add_or_replace_eos_token(tokenizer, eos_token=stop_words[0])
stop_words = stop_words[1:]
if tokenizer.eos_token_id is None:
self._add_or_replace_eos_token(tokenizer, eos_token="<|endoftext|>")
if tokenizer.pad_token_id is None:
tokenizer.pad_token = tokenizer.eos_token
logger.info_rank0(f"Add pad token: {tokenizer.pad_token}")
if stop_words:
try:
num_added_tokens = tokenizer.add_special_tokens(
dict(additional_special_tokens=stop_words), replace_additional_special_tokens=False
)
except TypeError:
num_added_tokens = tokenizer.add_special_tokens(dict(additional_special_tokens=stop_words))View on GitHub (pinned to f28afaf635)
Solutions
- If you authored the template, provide stop_words=["<your_eos>"] so the first entry can replace the EOS token.
- If you do not need EOS replacement, remove replace_eos=True from your register_template call.
- If using a built-in template, report the misconfiguration upstream — built-in templates with replace_eos always ship stop_words.
Example fix
# before register_template(name="custom", ..., replace_eos=True) # after register_template(name="custom", ..., stop_words=["<|im_end|>"], replace_eos=True)
Defensive patterns
Strategy: validation
Validate before calling
def template_eos_config_ok(template) -> bool:
return not template.replace_eos or bool(template.stop_words) Prevention
- When setting replace_eos=True, always pass stop_words and put the intended EOS first.
- Smoke-test get_template_and_fix_tokenizer(tokenizer, DataArguments(template=...)) right after registering a custom template.
When it happens
Trigger: Using a built-in template that declares replace_eos=True but has no stop_words (only possible if the template registry entry was misconstructed, e.g. by a plugin or a modified template); creating a custom template with replace_eos=True while omitting the stop_words argument in register_template; unpickling or copying a Template object whose stop_words were cleared.
Common situations: Writing a custom template and setting replace_eos=True without understanding it consumes stop_words[0] as the new EOS; library version changes that renamed the parameter; subclassing Template and overriding stop_words with None.
Related errors
- Empty formatter should not contain any placeholder.
- A placeholder is required in the string formatter.
- Input must be string, set[str] or dict[str, str], got {type(
- Template {name} already exists.
- Expected a string, got {value}
AI-assisted analysis of hiyouga/LlamaFactory@f28afaf635 (2026-08-14).
Data as JSON: /api/errors/335a78401c446d39.
Report an issue: GitHub.