Scunthorpe problem
A filter blocking harmless text because a banned word appears inside it, as when AOL's sign-up filter refused the English town name Scunthorpe in 1996.
The Scunthorpe problem is what happens when a filter blocks harmless text because a banned word appears inside it, as when AOL's sign-up filter refused the name of the English town of Scunthorpe, which contains a swear word. It comes from matching strings of letters instead of reading words in context. It happens in every language: everyday Spanish, French, German and Russian words contain strings from word lists, and code that lowercases text without a language's own rules adds more cases.
Where the name comes from
A man from Scunthorpe tried to open an AOL account. AOL's indecency filter would not accept his home town, because "Scunthorpe" contains a four-letter swear word. After talking to AOL's staff, he registered by typing "Sconthorpe" instead. The Scunthorpe Evening Telegraph reported the case on 9 April 1996, and the RISKS Digest, a forum on risks to the public in computers, recorded it on 25 April 1996 (volume 18, issue 07). Wikipedia's article on the problem dates the name to this incident.
How matching letters goes wrong
The simplest word filter checks whether any string on its list appears anywhere in the text. That fails in two ways:
- Blocking. The text is refused because a listed string sits inside a harmless word, as with Scunthorpe.
- Replacing. The filter swaps the string for a milder one. On 2 September 2008, The Telegraph described "The Clbuttic Mistake": filters that replace "ass" with "butt" turn "classic" into "clbuttic" and "assassination" into "buttbuttination".
Both have one cause: the filter sees letters, not words.
Why every language has it
A word list holds short strings, and short strings turn up inside other words in every language. Each of these everyday words contains a string that is a vulgar word in its own language:
| Everyday word | Language and meaning | Listed string inside it |
|---|---|---|
| classic, assignment | English | ass, a vulgar word |
| ridículo, artículo | Spanish: ridiculous, article | culo, a vulgar word |
| computador, reputação | Portuguese: computer, reputation | puta, a vulgar word |
| ridicule, particulier | French: ridiculous, particular | cul, a vulgar word |
| Nachbarschaft, Marsch | German: neighbourhood, march | arsch, a vulgar word |
| корабля | Russian: of the ship | бля, a vulgar word |
Words with endings add cases. A listed insult comes in many forms, so a rule that matches the start of a word to catch them also catches harmless words that start the same way. Text typed without its accents adds more.
Case rules depend on the language
Code that lowercases text with the default rules can turn a harmless word into a listed string, because languages differ in how they pair capital and small letters. Turkish is the best-known case: the word SIKICI (boring) in capitals becomes sikici, which contains the vulgar word sik. In Turkish, I lowercases to ı and İ to i, as Unicode's SpecialCasing.txt records, but default lowercasing maps I to i:
"SIKICI".toLowerCase(); // "sikici": I becomes i
"SIKICI".toLocaleLowerCase("tr-TR"); // "sıkıcı" (boring): I becomes ı
"İSTANBUL".toLowerCase(); // "i̇stanbul": i plus a combining dot (U+0307)
"SIKICI".lower() # 'sikici': str.lower() takes no locale
def tr_lower(s):
return s.replace("I", "ı").replace("İ", "i").lower()
tr_lower("SIKICI") # 'sıkıcı'
We ran these lines with Node.js 22.23.2 and Python 3.9.6 on 16 October 2026.
Word boundaries, and where they stop working
Matching whole words fixes English cases like these: the pattern \bass\b does not match "classic". In other languages it leaves three gaps:
- Endings. A listed insult appears with endings, so a whole-word pattern misses it. Matching the start of words catches those forms, but it also catches harmless words that start with the same letters.
- Missing accents. Text typed without its accents is a different string from the word it stands for, so a word-start pattern can match it when it would not match the accented word.
- JavaScript's
\b. It counts only A–Z, a–z, 0–9 and the underscore as word characters, so letters such as é, ñ, ö and ü, and every Cyrillic, Arabic or Devanagari letter, act as word edges. Python'sretreats them as letters.
/\bass\b/.test("classic"); // false: \b fixes this English case
/\bcafé\b/.test("café"); // false: é is not a word character for \b, so no boundary follows it
/\bass\b/.test("a donkey, also called an ass"); // true: a whole word, and harmless here
const listed = (word) => new RegExp(`(?<![\\p{L}\\p{N}])${word}(?![\\p{L}\\p{N}])`, "u");
listed("café").test("café"); // true: letters with accents count as letters
listed("ass").test("classic"); // false
The pattern \bass\b finds ass as a whole word in the donkey sentence, but it cannot tell a farm listing from an insult. That is the limit of every list.
The other direction: evasion
Lists also miss what they are meant to catch. A swear word written with a digit for a letter, with dots between its letters or with a lookalike letter from another alphabet is a different string. Each variant you add to the list is one more short string that can turn up inside a harmless word.
Fixes and their limits
- Allowlists. A list of harmless words the filter skips fixes the cases you know about. In languages with word endings, each harmless word has many forms, so the list grows with every ending.
- Language-aware lowercasing. Apply the language's own case rules before matching, as Turkish needs for I and İ. They cannot restore letters the writer never typed.
- Models that read the whole message. A classifier, or a language model asked to judge, scores the message as a whole instead of matching listed strings. That removes the cause described here, but models make their own mistakes in both directions, so measure them on your own content; see false positives.
No filter has zero false positives. Hold uncertain cases for a person instead of blocking them.
Test your own filter
Run each input through your filter and through any API you are evaluating. None of them should be blocked:
| Input | What it tests |
|---|---|
| Scunthorpe; classic; assignment | English words with a listed string inside |
| ridículo; artículo | Spanish words with a listed string inside |
| computador; reputação | Portuguese words with a listed string inside |
| ridicule; particulier | French words with a listed string inside |
| Nachbarschaft; Marsch | German words with a listed string inside |
| корабля | a Russian word with a listed string inside |
| SIKICI; İSTANBUL | Turkish lowercasing of I and İ |
| a donkey, also called an ass | a harmless sentence with a listed word, not an insult |
Then add the words on your own list with suffixes, digits, dots and lookalike letters. Those should be caught.
To try them on Detectivision, use the playground in the dashboard after you sign up, or send each one from your server:
curl https://api.detectivision.ai/api/v1/moderation \
-H "Api-Key: $DETECTIVISION_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "text_moderation", "input": "The class passed the assignment.", "mode": "detect"}'
The answer's flags lists each flag raised, with its confidence, and checked lists every flag that was looked for (responses). This page makes no claim about how any filter, ours included, scores these inputs: judge the answers yourself.
What Detectivision does not do
- It does not mark words. Its text flags, profanity and insult among them, judge the whole message. No word positions or masked copy come back, so hide, hold or reject the whole message.
- It does not read every language. Text results are reliable in English, Spanish, Portuguese, French, German, Italian, Turkish, Arabic, Russian and Hindi only (text moderation).
- It does not promise zero false positives. No filter can; measure ours on your own content.
For the product side, see the profanity filter API, Turkish offensive language detection and toxicity detection.
Then try your own words in the playground.
Frequently asked
What is the Scunthorpe problem?
It is a filter blocking or changing harmless text because a banned string of letters appears inside it. The name comes from Scunthorpe, an English town whose name contains a swear word: in a case reported in April 1996, AOL's sign-up filter would not accept it as a user's home town. It happens wherever software matches letters instead of reading words.
How do you fix the Scunthorpe problem?
You can reduce it, not remove it. Match whole words instead of parts of words, keep an allowlist of harmless words your users write, lowercase text with the rules of its language, and hold uncertain cases for a person instead of blocking them. A model that scores the whole message does not match listed strings, but it makes mistakes of its own, so measure it on your own content.
Is there a Scunthorpe problem detector?
There is no standard tool. Build a test set instead: harmless words that contain the strings on your list, the same words in capitals and typed without accents, and the misspellings your users write. Run it after every change to your filter, and add each false positive that users report. The table on this page is a place to start.
Does matching whole words solve the Scunthorpe problem?
In English it fixes cases like Scunthorpe and "classic". In languages whose words take endings it is not enough: swear words take endings, so lists end up matching the start of words, which catches harmless words that start the same way. In JavaScript the word-boundary pattern also treats letters such as é, ñ, ö and ü as non-letters, so a listed word can match inside a longer word or never match at all.
Find swear words in chat, comments and usernames in ten languages with one API call: separate flags for profanity and insults, no word list to maintain.
Detect insults, hate speech, racism, sexism and sexual harassment in Turkish text, and in English too, with one API call and a confidence per flag.
Check comments and chat messages for insults, hate, harassment and self-harm in ten languages, with a flag for each kind of toxicity.
A false positive is harmless content a moderation system flags. Why they happen, how to measure them on your own data, and how thresholds and review help.
A content moderation API checks the text or images you send and says what it found and how sure it is. How requests, results, prices and decisions work.
- Scunthorpe problem
- AOL censors British town's name! (Clive Feather, The RISKS Digest, volume 18, issue 07, 25 April 1996)
- The Clbuttic Mistake: When obscenity filters go wrong (The Telegraph, 2 September 2008; Internet Archive copy)
- SpecialCasing.txt, Unicode Character Database 18.0.0
- Word boundary assertion: \b, \B
- String.prototype.toLocaleLowerCase()
- Built-in types, str.lower()
- re, regular expression operations
- Text moderation
- Responses
- Service status, GET /api/v1/status