TypeError: 'NoneType' object is not iterable is a Python error that hits when you try to loop over None — usually because a function returned None when you expected a list, dict, or other sequence. It's one of the most common Python runtime errors in production, and it's almost always preventable with a small amount of defensive programming.
This guide walks through what triggers the error, how to spot the source in a stack trace, and the fixes that actually prevent it from happening again.
What the error means
In Python, some built-ins and functions are iterable — you can loop over them with for item in x:. Lists, tuples, dicts, strings, and sets are iterable. None is not. So this code:
items = get_items()
for item in items:
print(item)
throws TypeError: 'NoneType' object is not iterable when get_items() returns None instead of a list.
The error message is blunt but accurate: you're asking Python to iterate over None, which isn't iterable. The hard part isn't understanding the error — it's finding which function returned None and why. That's where error tracking and the ability to read the stack trace saves hours.
The common causes
Nearly every instance of this error traces back to one of these patterns:
- A function explicitly returns
None. The most common case. A helper function doesn't return anything, so Python returnsNoneby default, and the caller tries to loop over it.
def fetch_user_preferences(user_id):
if not is_authorized(user_id):
return # Oops: returns None
preferences = db.query(user_id)
return preferences.to_list()
- A dictionary lookup or conditional returns
None.dict.get()andlist.pop()returnNoneif the key doesn't exist, and conditional branches may not return anything at all.
config = settings.get("features") # None if "features" not present
for feature in config: # TypeError
enable(feature)
- A library function returns
Noneon error or empty result. Some database queries, HTTP calls, or parsing functions returnNoneto signal "nothing found" or "error occurred."
rows = db.query("SELECT * FROM users WHERE id = ?", user_id)
for row in rows: # TypeError if query returns None instead of empty list
process(row)
- A regex or string method returns
Nonewhen it doesn't match.re.search()andre.match()returnNoneif the pattern doesn't find a match.
match = re.search(r"(\d+)", text)
for group in match.groups(): # TypeError if match is None
print(group)
When working with APIs or database libraries you're unfamiliar with, read the docs for what the function returns on "no match" — is it None, an empty list, an exception, or something else? One wrong assumption will cost you a production error.
How to fix it
Guard with explicit None checks
The simplest fix is to check explicitly:
items = get_items()
if items is not None:
for item in items:
process(item)
Return an empty sequence instead of None
This is the most Pythonic fix. Let the caller always get an iterable — empty if nothing found, populated otherwise. Change your functions to return [] or {} instead of None:
def fetch_user_preferences(user_id):
if not is_authorized(user_id):
return [] # Empty list, not None
preferences = db.query(user_id)
return preferences.to_list()
Now the caller doesn't have to check:
for pref in fetch_user_preferences(user_id):
apply(pref)
Use or and a default value
A common idiom: return None for simplicity, but convert it at the callsite:
items = get_items() or []
for item in items:
process(item)
This works because None or [] evaluates to []. Be careful, though — [] is falsy too, so [] or ["default"] will return the default even if you have an empty list. Use this pattern only when None and empty are truly equivalent.
Check return types with assertions (for defensive code)
In production-critical code, fail loudly if a function returns the wrong type:
items = get_items()
assert isinstance(items, (list, tuple)), f"Expected list/tuple, got {type(items)}"
for item in items:
process(item)
This turns a cryptic NoneType error into a clear assertion failure that pinpoints the bug.
Handle library functions explicitly
When using a library that returns None on error, wrap it:
# db.query() returns None on error, empty list on success
rows = db.query("SELECT * FROM users") or []
for row in rows:
process(row)
Or use exception handling if the library raises instead:
try:
rows = db.query("SELECT * FROM users")
except QueryError:
rows = []
for row in rows:
process(row)
Using or [] as a catch-all is tempting but masks bugs. If a function is supposed to return a list and returns None, that's a real failure — the function logic is broken. Don't hide it with defaults; fix the function. Use or only when None and empty are truly the same to your code.
Finding it in production
Locally you can add print statements. In production you have a stack trace and a user who's already moved on. This is where a good error tracking system earns its place: it captures the exact line that failed, the breadcrumbs showing what the user did, and the process for debugging production errors becomes systematic rather than reactive.
You'll see something like:
File "myapp/views.py", line 42, in process_order
for item in get_items():
TypeError: 'NoneType' object is not iterable
From there, you look at get_items() and trace backward to find which input caused it to return None. With the full stack trace and context, you'll often spot the fix in seconds.
Prevention through code review
The best defense is catching these before they ship. In code review, watch for:
- Functions that don't have an explicit
returnstatement in all branches — they'll returnNoneimplicitly. - Calls to
.get()or similar lookup functions without a check. - Assumptions about return types from libraries you haven't used before.
A quick comment — "this can return None" — saves a production incident.
Start tracking errors in minutes
Catch 'NoneType' errors before your users do. Point the Sentry SDK at LightTrace to get production stack traces, breadcrumbs, and affected user counts in your dashboard.
The good news: this error is almost entirely preventable with defensive programming — checking for None, returning empty sequences instead, and asserting types in critical paths. Once you adopt these patterns, you'll rarely see it again.