Glossary · Published · Updated

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 wordLanguage and meaningListed string inside it
classic, assignmentEnglishass, a vulgar word
ridículo, artículoSpanish: ridiculous, articleculo, a vulgar word
computador, reputaçãoPortuguese: computer, reputationputa, a vulgar word
ridicule, particulierFrench: ridiculous, particularcul, a vulgar word
Nachbarschaft, MarschGerman: neighbourhood, marcharsch, 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:

Node.js
"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)
Python
"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:

  1. 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.
  2. 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.
  3. 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's re treats them as letters.
Node.js
/\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:

InputWhat it tests
Scunthorpe; classic; assignmentEnglish words with a listed string inside
ridículo; artículoSpanish words with a listed string inside
computador; reputaçãoPortuguese words with a listed string inside
ridicule; particulierFrench words with a listed string inside
Nachbarschaft; MarschGerman words with a listed string inside
корабляa Russian word with a listed string inside
SIKICI; İSTANBULTurkish lowercasing of I and İ
a donkey, also called an assa 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
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.

Start the free trial

Then try your own words in the playground.

Questions

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.

Related articles
  • 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.

Sources
  1. Scunthorpe problem, Wikipedia, read on 16 October 2026
  2. AOL censors British town's name! (Clive Feather, The RISKS Digest, volume 18, issue 07, 25 April 1996), The RISKS Digest, read on 16 October 2026
  3. The Clbuttic Mistake: When obscenity filters go wrong (The Telegraph, 2 September 2008; Internet Archive copy), The Telegraph, read on 16 October 2026
  4. SpecialCasing.txt, Unicode Character Database 18.0.0, Unicode, read on 16 October 2026
  5. Word boundary assertion: \b, \B, MDN Web Docs, read on 16 October 2026
  6. String.prototype.toLocaleLowerCase(), MDN Web Docs, read on 16 October 2026
  7. Built-in types, str.lower(), Python Software Foundation, read on 16 October 2026
  8. re, regular expression operations, Python Software Foundation, read on 16 October 2026
  9. Text moderation, Detectivision, read on 16 October 2026
  10. Responses, Detectivision, read on 16 October 2026
  11. Service status, GET /api/v1/status, Detectivision, read on 16 October 2026