Tokenization Explained
Theme
Palette
Download
Caption (tap to copy)
📖 Deep dive (full written explanation)
This final post is a pre-mortem, and the cover states the thesis bluntly: tokenization bugs don't throw errors. They silently double your cost, truncate your context, or make the model fail a task it should ace — and you never see why. After four posts showing how accessible tokenization is, this one supplies the discipline that keeps that accessibility from becoming overconfidence.
The five mistakes are ordered by where they bite in a typical workflow — estimating cost, fitting a context window, chunking, validating input, and serving non-English users — so the post doubles as a checklist you can walk top to bottom before shipping.
The first mistake is the most pervasive: estimating tokens by counting words. The 0.75-words-per-token rule holds only for clean English prose and collapses for code, URLs, JSON, emoji, and other languages. A reader who budgets a context window or an API spend off word counts will be wrong, sometimes by a factor of two or more.
The broader principle is 'always count, never guess'. Tokenization is deterministic and the count is one cheap function call away, so there's no excuse for estimating. This mistake is the direct consequence of forgetting the first post's lesson that token count is not word count, and it's the root cause of most surprise bills.
The code slide makes the words-versus-tokens gap impossible to dismiss by contrasting two five-token-looking strings. A simple five-word sentence of prose encodes to about five tokens, while a compact JSON object that also looks like roughly five 'words' encodes to triple that, because every brace, quote, colon, and bracket fragments heavily.
The lesson is visceral: visual similarity tells you nothing about token cost. A developer who runs this once will never again eyeball a JSON-heavy prompt and assume it's cheap. It also reinforces the why-it-matters post's bar chart, turning that relative comparison into a concrete, reproducible measurement.
The second mistake targets a subtle off-by-a-few error: forgetting that the model injects special tokens you didn't write. When you carefully trim a prompt to exactly fit a 512-token window but count only your own content, the added [CLS], [SEP], chat-role markers, and separators push you over, causing rejection or silent truncation.
The fix is to count the way the model actually sees the input — with special tokens included. This connects directly to the how-it-works post, where special tokens were shown occupying real vocabulary IDs, and to the code post, where decoding revealed them. Here that knowledge becomes a concrete safeguard against an error that's maddening to debug because the numbers look right.
The accompanying code makes the hidden tokens visible by encoding the same word twice — once without special tokens and once with — and printing both lengths. The single content token becomes three once [CLS] and [SEP] are added, a 200% overhead on a short input.
The takeaway scales: the special-token overhead is fixed per sequence, so it matters most when you're packing many short inputs or sitting right at the context limit. Using add_special_tokens=True to measure exactly what the model receives is the simple discipline that prevents the boundary errors this mistake describes.
The third mistake is truncating or chunking by character offset instead of token boundary. Slicing text at an arbitrary character can cut a multi-byte Unicode character in half, producing mojibake, or split a word-piece into a meaningless fragment. Naive character chunking can also sever a sentence so each chunk loses the context it needed.
The fix is to operate on token boundaries: truncate to a token count and chunk on natural units like sentences. This matters most for non-English text and for RAG pipelines, where careless chunking quietly corrupts the very documents you're trying to retrieve over. Respecting token boundaries keeps every chunk valid and decodable.
The comparison diagram dramatizes character-cutting versus token-cutting side by side. Cutting by characters can split a token in half, produce broken bytes or mojibake, and lose context mid-word. Cutting by tokens lands on a clean boundary, yields valid decodable text, and lets you align chunks to sentences.
The contrast is stark on purpose — it's the clearest way to show that the unit you choose for truncation has real correctness consequences, not just stylistic ones. A reader who internalizes this will reach for the tokenizer's own truncation utilities rather than Python string slicing whenever length matters.
The fourth mistake is conflating character limits with token limits, which causes both false rejections and dangerous acceptances. The two simply don't track: one emoji can be two to four tokens, a single Chinese character is often one or two, and a long English word can be several tokens — so a character-based length check is wrong in both directions.
Validating with character counts will reject perfectly valid short-but-token-dense input and, worse, accept input that's actually over the model's real limit. The fix is to validate length using the actual tokenizer, the same one the model uses, so your guardrail matches the model's true constraint.
The validation code shows the correct pattern: a fits function that encodes the text with the real tokenizer, compares the token count against the limit, and returns both the verdict and the count so the caller can act on it. Raising a clear error with the actual token number turns a silent truncation into an actionable failure.
The key design choice is using len(tok.encode(text)) — the model's own tokenizer — rather than len(text). This is the concrete realization of 'char limits are not token limits', and it's the kind of input guard that belongs at the boundary of any production LLM service to prevent both rejected-valid and accepted-oversized inputs.
The fifth mistake is ignoring the non-English token tax in product decisions. If your application serves users in other languages, an English-trained tokenizer quietly charges them two to three times the tokens for the same meaning, consuming their context window and inflating their cost — an invisible inequity baked into the system.
The fixes are practical: budget token usage per language rather than assuming a single average, test with real multilingual samples instead of English placeholders, and for global products consider models whose tokenizers were trained on more balanced multilingual data. This connects back to the language-tax slide in the why-it-matters post, turning an observation into a deployment-time discipline.
The pre-flight checklist gathers all five mistakes into one scannable list: count tokens rather than estimating from words, include special tokens in the count, truncate on token boundaries, validate length in tokens rather than characters, and budget extra for non-English text. It's designed to be screenshotted and pinned next to a project.
Each item maps directly to one of the five mistakes, so the slide works as both a summary and a recurring reference. The intent is that a reader runs through these five checks before shipping any LLM feature, the way a pilot runs a pre-flight list, catching the silent tokenization failures before they reach production.
The closing card wraps the five-post arc and points to the next day. Having covered what a token is, why tokenization matters, how BPE builds a vocabulary, how to run tokenizers in code, and how tokenization quietly fails, the reader now has a complete, grounded foundation in one of the most under-appreciated parts of the LLM stack.
The teaser keeps momentum by promising to go deeper into the NLP and LLM stack, signaling that this thorough treatment of tokenization is a launchpad into the embeddings, attention, and model-internals topics the series tackles next.