PythonMastery
reference 3 min read · lesson 7 of 45 in Errors

AttributeError: 'NoneType' object has no attribute 'X'

1 · The lesson

read

What this error means

You called a method or accessed an attribute on None. None is Python's "absence of a value" — it has none of the methods you'd expect on a string, list, dict, or custom object. Whatever you thought you had, you actually have None. The fix is to find out why it's None, not to add a try/except.

When you see it

python
Traceback (most recent call last):
  File "main.py", line 3, in <module>
    name = user.get("name").upper()
AttributeError: 'NoneType' object has no attribute 'upper'

Why it happens

The classic trap: chaining a method onto something that can return None.

python
user = {"id": 1}
name = user.get("name").upper()       # .get returns None — boom

Other common sources:


  • re.match(...) / re.search(...) returns None on no match.

  • dict.get(key) returns None when the key is missing (and you didn't pass a default).

  • An ORM .first() returns None when there's no row.

  • A function forgot to return, so it implicitly returned None.

  • Chained .find(...) calls on BeautifulSoup that didn't match.

How to fix it

Pass a default to dict.get:

python
name = user.get("name", "").upper()       # default "" so .upper() always works
+ setup added so this can run · defines user
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

user = _AutoMock('user')

Guard with if x is not None when None is genuinely meaningful:

python
match = re.match(r"\d+", text)
if match is not None:
    number = int(match.group())
+ setup added so this can run · defines text, re
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

text = _AutoMock('text')
re = _AutoMock('re')

Use the walrus operator to test and reuse:

python
if (row := db.first()) is not None:
    print(row.name)
+ setup added so this can run · defines row, db
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

row = _AutoMock('row')
db = _AutoMock('db')

Fix the upstream function — if a function should always return a string, give it a sensible fallback instead of letting it return None:

python
def display_name(user):
    return user.get("name") or "anonymous"      # never returns None

When you'd actually see this in real code

  • API response missing an optional field: data.get("address").get("city") blows up when address is absent.
  • os.environ.get("DATABASE_URL").split("@") when the env var isn't set.
  • A BeautifulSoup scrape: soup.find("div", class_="price").text when the div is missing on some pages.
  • A database .first() returning None because the row was just deleted.

See Also