Python TypeError: argument of type 'NoneType' is not iterable
A deep-dive into one of the most common Python errors β when you try to iterate over or check membership of a None value, and how to systematically fix it.
Python TypeError: argument of type 'NoneType' is not iterable
Introduction
If you have spent any meaningful time writing Python, you have almost certainly run into this error:
TypeError: argument of type 'NoneType' is not iterableIt is one of those errors that looks cryptic at first but reveals a very specific problem once you understand what Python is telling you. The error occurs when you use the in keyword on a value that is None, or when you pass None to a function that expects an iterable like list(), tuple(), or a loop.
I first encountered this error when building a trading signal pipeline in Lisbon. My function was supposed to return a filtered list of buy signals, but under certain market conditions it returned nothing at all β which Python interpreted as None. Downstream code tried to check if signal in signals, and the whole pipeline crashed.
Environment
This error is not version-specific. It appears in Python 3.6 through 3.12 and across all operating systems. Here is a minimal environment:
Python 3.12.3 (tags/v3.12.3:82c7b5b, Apr 20 2024, 13:13:12)
[GCC 13.2.0] on linuxNo third-party libraries are required to reproduce this error. It is pure Python.
Problem
Here is the simplest reproduction:
def find_signals(data: list) -> list | None:
"""Search for trading signals in data."""
results = []
for item in data:
if item.get("type") == "buy":
results.append(item)
# Forgot to return results!
# Implicit return None
signals = find_signals([{"type": "buy"}, {"type": "sell"}])
# This line crashes with TypeError
if "buy" in signals:
print("Found buy signals")Running this produces:
TypeError: argument of type 'NoneType' is not iterableThe function find_signals has an implicit return None because the return results line is missing or placed inside an if block that does not always execute.
Another common pattern:
def get_config(key: str) -> dict | None:
config = load_config_file()
return config.get(key) # Returns None if key is missing
config = get_config("database")
# Boom β TypeError
for setting in config:
print(setting)Analysis
The error has exactly one cause: the variable you are trying to use as an iterable is None. Python's in operator, for loops, list(), tuple(), sum(), and many other builtins all expect an iterable argument. When they receive None, they raise TypeError.
There are three common scenarios:
Scenario 1: A function returns None implicitly. Every Python function returns None if it does not hit a return statement with a value. If a branch in your function misses the return, you get None.
Scenario 2: A method returns None explicitly. Methods like dict.update(), list.append(), and set.add() all return None by design. If you chain these calls or assign their result, you get None.
# dict.update() returns None
config = config.update(new_values) # config is now NoneScenario 3: A dictionary.get() call with no default. dict.get(key) returns None if the key is missing. If you then iterate over the result, you crash.
Solution
The fix depends on the scenario. Here are the three most effective strategies.
Strategy 1: Guard with an explicit None check before iterating.
signals = find_signals(data)
if signals is not None:
if "buy" in signals:
print("Found buy signals")Strategy 2: Use a default value instead of None.
def find_signals(data: list) -> list:
results = []
for item in data:
if item.get("type") == "buy":
results.append(item)
return results # Always returns a list, never None
config = get_config("database") or {}
for setting in config:
print(setting)Strategy 3: Use a type checker like mypy to catch the issue before runtime.
def find_signals(data: list) -> list: # mypy now enforces this
results = []
for item in data:
if item.get("type") == "buy":
results.append(item)
# mypy will warn if any path misses the return
return resultsRunning mypy on your project:
$ mypy --strict your_script.py
your_script.py:5: error: Missing return statement [return-value]This catches the bug before the code ever runs.
Lessons Learned
- Always check your return paths. Every branch of a function should explicitly return a value if the caller expects one.
- Use
orfor safe defaults.config = get_config("database") or {}preventsNonefrom propagating. - Type hints plus mypy are your best defense. A strict mypy configuration catches implicit
Nonereturns at development time rather than in production. - When you see this error, trace the variable backward. The variable that is
Nonealmost always comes from a function that forgot to return a value.
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.