Fixing EOS Token Errors: Why Your Fine-Tuned Model Won't Stop Generating
Quick Answer / TL;DR
If your model hallucinates a new "User:" prompt and starts talking to itself after answering, your tokenizer failed to append the correct End of Sequence (EOS) token during training. You must explicitly set tokenizer.pad_token = tokenizer.eos_token and ensure your chat_template maps to the correct model-specific token (e.g., <|eot_id|> for Llama 3 vs. <|im_end|> for Qwen/ChatML).
The "Infinite Conversation" Bug
You just spent 8 hours running Supervised Fine-Tuning (SFT) on an 8B parameter model. You load it up for inference and ask it a question. The model answers perfectly, but instead of stopping, it outputs a newline, writes a brand new question from a fabricated user, and proceeds to answer that too.
This happens because language models are fundamentally next-token predictors. When the model reaches the end of its intended answer, it needs a mathematical stop sign (the EOS token) to trigger the inference engine to halt. If your training dataset was improperly formatted, the model never learned to associate the end of its reasoning trace with that stop sign.
The Fix: Aligning Tokenizer Templates
Base models have different vocabularies. You cannot apply a generic ChatML format to a Llama 3 architecture without causing severe tokenization mismatches. Before passing your dataset into the SFTTrainer, you must enforce the correct special tokens.
from transformers import AutoTokenizer
model_id = "meta-llama/Meta-Llama-3-8B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_id)
# 1. Verify what the model actually expects
print("Model EOS Token:", tokenizer.eos_token)
# Llama 3 outputs: <|eot_id|> (NOT <|end_of_text|>)
# 2. Fix the padding index (Crucial for batched SFT)
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
# Enforce right padding so the EOS token stays at the absolute end
tokenizer.padding_side = "right"Applying the Chat Template
Never manually concatenate strings like "Assistant: " + text + "\n". Always map your JSONL data through the tokenizer's native apply_chat_template method. This guarantees the correct EOS token is baked into the tensor arrays during the forward pass.
def format_dataset(examples):
# This automatically appends the correct EOS token per the model's config
texts = [
tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=False
)
for messages in examples["messages"]
]
return {"text": texts}Related Cookbooks
LoRA from Scratch in PyTorch: Implementation with Code
Implement LoRA (low-rank adaptation) in pure PyTorch: the low-rank A and B matrices, initialization, merging weights, and a training example.
Train Custom Tokens with LoRA: resize_token_embeddings and trainable_token_indices
Add new special tokens to a model and train them with LoRA. Covers resize_token_embeddings, modules_to_save, and PEFT trainable_token_indices, with code.
Fix "element 0 of tensors does not require grad" (LoRA + Gradient Checkpointing)
LoRA training crashes with gradient_checkpointing_enable()? The fix: enable_input_require_grads() and use_reentrant=False. Copy-paste code for PEFT and Transformers.