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

TypeError: 'NoneType' object is not iterable

1 · The lesson

read

What this error means

You tried to loop over (or unpack, or *-spread) something that turned out to be None. None is Python's "nothing" value — it has no elements, so iteration is meaningless. Almost every time you see this, a function you expected to return a list/dict/tuple actually returned None, usually because a return statement was missing or skipped.

When you see it

python
Traceback (most recent call last):
  File "main.py", line 8, in <module>
    for name in get_names():
TypeError: 'NoneType' object is not iterable

Why it happens

A Python function that "falls off the end" without hitting a return returns None. So does a function whose return is hidden inside an if branch that didn't run. The caller then tries to iterate over None, and Python complains.

python
def get_names(active=True):
    if active:
        return ["alice", "bob"]
    # implicit `return None` when active is False

for name in get_names(active=False):   # boom
    print(name)

Other common triggers: list-mutating methods (list.sort(), list.append(), list.reverse()) return None, not the list. Assigning their result and then iterating gives the same error.

How to fix it

Add the missing return, or return an empty collection so the caller can always iterate:

python
def get_names(active=True):
    if active:
        return ["alice", "bob"]
    return []                       # never return None for a "list-returning" function

Don't assign the result of in-place methods:

python
nums = [3, 1, 2]
nums.sort()                # mutates in place — returns None
# for n in nums.sort():    # wrong: iterates over None
for n in nums:             # right
    print(n)

# If you want a new sorted list without mutating:
for n in sorted(nums):
    print(n)

Guard the call site if None is genuinely a valid return value:

python
result = lookup(user_id)
if result is not None:
    for row in result:
        ...
+ setup added so this can run · defines lookup, user_id
# 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,)

def lookup(*_a, **_kw):
    print('-> lookup() called')
    return _AutoMock('lookup()')
user_id = _AutoMock('user_id')

When you'd actually see this in real code

  • A find_user() helper returns None when the user doesn't exist, but the caller iterates blindly.
  • re.match(...) returns None on no match — and you tried to unpack its groups.
  • dict.get("key") returns None when the key is missing and you looped over it expecting a list.
  • You wrote names = names.sort() instead of names.sort() or names = sorted(names).

See Also