Cognitive distortions are the mental shortcuts that lie to us. "I failed once, so I'll always fail." "Everyone is judging me." "If it's not perfect, it's worthless."
Aaron Beck identified 15+ of these patterns in the 1960s. They're the core target of Cognitive Behavioral Therapy (CBT). And they're surprisingly detectable with code — because distortions have linguistic signatures.
In this tutorial, I'll show you how to build a detector that identifies 10 cognitive distortions from a text input, in under 60 lines of Python. No ML model. No API key. Just pattern matching on the linguistic markers that therapists look for.
The 10 Distortions
Here are the patterns we'll detect, with their linguistic signatures:
| Distortion | Signature | Example |
|---|---|---|
| All-or-Nothing | "always", "never", "completely", "total" | "I always mess up" |
| Overgeneralization | "always", "never", "every time" | "Every time I try, I fail" |
| Mental Filter | "only", "just", "nothing but" | "It was only the bad part" |
| Disqualifying Positive | "but", "doesn't count", "doesn't matter" | "She said I did well, but she was just being nice" |
| Mind Reading | "they think", "everyone knows", "people are" | "They think I'm stupid" |
| Fortune Telling | "will", "going to", "I'll never" | "I'll never get this job" |
| Magnification | "terrible", "awful", "disaster", "worst" | "This is a complete disaster" |
| Emotional Reasoning | "I feel", therefore it is | "I feel guilty, so I must be guilty" |
| Should Statements | "should", "must", "have to", "ought" | "I should be better at this" |
| Labeling | "I am a", "I'm a", "he is a" | "I'm a failure" |
The Code
import re
from dataclasses import dataclass
@dataclass
class Distortion:
name: str
description: str
patterns: list[str]
intervention: str
DISTORTIONS = [
Distortion("All-or-Nothing Thinking", "Seeing things in black-white categories",
[r"(always|never|completely|totally|absolute)"],
"Is it truly 100% one way? Look for the middle ground."),
Distortion("Overgeneralization", "Viewing a single event as a never-ending pattern",
[r"(everys*time|always|nevers*again)"],
"One event ≠ a pattern. What's the evidence for 'every time'?"),
Distortion("Mental Filter", "Focusing exclusively on negative aspects",
[r"(only|just|nothings*but)"],
"What positive aspects are you filtering out?"),
Distortion("Disqualifying Positive", "Rejecting positive experiences as if they don't count",
[r"(buts+(?:its+)?doesn'?ts+count|doesn'?ts+matter|justs+beings+nice)"],
"Can you accept the positive at face value?"),
Distortion("Mind Reading", "Assuming you know what others are thinking",
[r"(theys+think|everyones+(?:think|know)|peoples+ares+thinking)"],
"What's the evidence? Did they say it, or are you assuming?"),
Distortion("Fortune Telling", "Predicting negative outcomes as fact",
[r"(I'?lls+never|goings+tos+fail|wills+never)"],
"Can you predict the future? What's a more likely outcome?"),
Distortion("Magnification", "Exaggerating the importance of negatives",
[r"(terrible|awful|disaster|catastrophe|worst|ends+ofs+thes+world)"],
"How bad is it really, on a 1-10 scale?"),
Distortion("Emotional Reasoning", "Assuming feelings reflect objective reality",
[r"(Is+feels+w+.*?(?:so|therefore).*?(?:must|am|means))"],
"Feelings are data, but not proof. What would evidence say?"),
Distortion("Should Statements", "Rigid rules about how things 'should' be",
[r"(should|must|haves+to|oughts+to|shouldn'?t)"],
"Replace 'should' with 'prefer' or 'choose to'. What happens?"),
Distortion("Labeling", "Defining yourself by a single event",
[r"(I'?ms+as+w+|Is+ams+as+w+|hes+iss+as+w+|shes+iss+as+w+)"],
"You are not one event. Describe the behavior, not the person."),
]
def detect_distortions(text: str) -> list[dict]:
"""Detect cognitive distortions in text. Returns list of matches."""
text_lower = text.lower()
results = []
for d in DISTORTIONS:
for pattern in d.patterns:
matches = re.findall(pattern, text_lower)
if matches:
results.append({
"distortion": d.name,
"description": d.description,
"matched": matches,
"intervention": d.intervention,
})
break # one match per distortion is enough
return results
Testing It
thought = "I always mess up. They think I'm a failure. I should just quit."
for r in detect_distortions(thought):
print(f"⚠️ {r['distortion']}")
print(f" Matched: {r['matched']}")
print(f" Reframe: {r['intervention']}")
print()
Output:
⚠️ All-or-Nothing Thinking
Matched: ['always']
Reframe: Is it truly 100% one way? Look for the middle ground.
⚠️ Mind Reading
Matched: ['they think']
Reframe: What's the evidence? Did they say it, or are you assuming?
⚠️ Should Statements
Matched: ['should']
Reframe: Replace 'should' with 'prefer' or 'choose to'. What happens?
⚠️ Labeling
Matched: ["i'm a failure"]
Reframe: You are not one event. Describe the behavior, not the person.
4 distortions detected from one sentence. That's the power of linguistic pattern matching — distortions leave traces in language.
Why This Works
CBT therapists are trained to listen for these exact linguistic markers. The patterns above aren't arbitrary — they come from Beck's original taxonomy and decades of clinical literature. When someone says "I always fail," the word "always" is a linguistic marker of all-or-nothing thinking. When someone says "they think I'm stupid," the phrase "they think" is a marker of mind reading.
You don't need a neural network for this. The distortions are rule-based by definition — that's what makes them teachable to patients in therapy. If they were fuzzy ML predictions, CBT wouldn't work as a structured, manualized therapy.
Going Further
This 60-line detector is the core of a larger open-source toolkit I built — 36 free CBT tools covering procrastination patterns, attachment styles, safety behaviors, core beliefs, and more. The full toolkit includes:
- A hosted API (free, no auth required)
- Browser-based tools (no install)
- PDF workbooks
- A Python package
The full cognitive distortion detector with 15 patterns, severity scoring, and JSON API output is at: github.com/alexcColeDev/cbt-toolkit
The Honest Part
226 people have cloned this repo. 2 have starred it. 0 have paid for the optional $1 supporter tier.
I'm sharing this not as a success story, but as a build-in-public data point. The toolkit works — the detector above catches real distortions. But "works" and "converts" are different problems. I'm still figuring out the second one.
If you found this tutorial useful, the repo has 35 more tools like this. No signup, no email gate, no paywall. Just code.
Tags: python, tutorial, mentalhealth, opensource
Top comments (0)