Fix: Python TypeError: 'NoneType' object is not subscriptable
Quick Answer
Learn why Python raises TypeError NoneType object is not subscriptable and how to fix it with practical solutions for functions, dictionaries, lists, APIs, and regex.
The Error
You run your Python script and get this traceback:
TypeError: 'NoneType' object is not subscriptableIt might appear with a specific line reference like this:
Traceback (most recent call last):
File "app.py", line 12, in <module>
value = result[0]
TypeError: 'NoneType' object is not subscriptableThis error means you tried to use square brackets ([]) on a variable that is None. Python does not allow indexing or key access on None because it has no items to look up. The variable you expected to hold a list, dictionary, or tuple is actually None.
The fix depends on why the variable is None in the first place. Below are the most common causes and their solutions.
Why This Happens
“Subscriptable” means an object supports the [] operator — lists, dicts, tuples, and strings all do. None does not.
When you write some_var[0] or some_var["key"], Python calls the object’s __getitem__ method. None has no such method, so Python raises a TypeError.
The real question is: why is the variable None? Here are the typical reasons:
- A function returns
Noneimplicitly (noreturnstatement, or a barereturn). - A list method like
.sort(),.append(), or.reverse()returnsNoneinstead of the modified list. - A dictionary lookup using
.get()returnsNonefor a missing key, and you immediately subscript the result. - An API response or JSON parse returns
Nonefor a missing field. - A regex match returns
Nonewhen the pattern does not match. - A chained method call where one step returns
None.
Each of these has a different fix. Let’s go through them.
Fix 1: Handle Functions That Return None Implicitly
This is the most common cause. In Python, any function that does not explicitly return a value returns None.
Broken code:
def get_user(user_id):
user = database.find(user_id)
if user:
print(f"Found user: {user['name']}")
# No return statement!
result = get_user(42)
print(result["name"]) # TypeError: 'NoneType' object is not subscriptableThe function prints the user but never returns it. So result is None.
Fixed code:
def get_user(user_id):
user = database.find(user_id)
if user:
return user
return None
result = get_user(42)
if result is not None:
print(result["name"])Always check your function’s return paths. A common mistake is having a return inside an if block but nothing for the else case:
def find_item(items, target):
for item in items:
if item["id"] == target:
return item
# Reaches here if no match — returns None implicitlyFix: Add an explicit return or raise an exception at the end:
def find_item(items, target):
for item in items:
if item["id"] == target:
return item
raise ValueError(f"Item {target} not found")Pro Tip: Enable type hints and use a tool like
mypyto catch these issues before runtime. Annotating your function asdef get_user(user_id: int) -> dict:will flag missing returns at analysis time, saving you from debuggingNoneTypeerrors in production. This is especially useful in larger codebases where functions are called from many places and implicitNonereturns are easy to miss.
Fix 2: Stop Subscripting List Method Results
Python list methods like .sort(), .append(), .reverse(), .extend(), and .insert() modify the list in place and return None. This catches many developers off guard.
Broken code:
numbers = [3, 1, 4, 1, 5]
sorted_numbers = numbers.sort()
print(sorted_numbers[0]) # TypeError: 'NoneType' object is not subscriptablenumbers.sort() sorts the list in place and returns None. So sorted_numbers is None.
Fixed code — option A (use the in-place method correctly):
numbers = [3, 1, 4, 1, 5]
numbers.sort()
print(numbers[0]) # 1Fixed code — option B (use sorted() to get a new list):
numbers = [3, 1, 4, 1, 5]
sorted_numbers = sorted(numbers)
print(sorted_numbers[0]) # 1The same pattern applies to other in-place methods:
# Wrong — .append() returns None
items = [1, 2, 3].append(4)
print(items[0]) # TypeError
# Right
items = [1, 2, 3]
items.append(4)
print(items[0]) # 1
# Wrong — .reverse() returns None
reversed_list = [1, 2, 3].reverse()
print(reversed_list[0]) # TypeError
# Right
items = [1, 2, 3]
items.reverse()
print(items[0]) # 3
# Or use slicing: reversed_list = items[::-1]If you are coming from JavaScript, this is a common stumbling block. In JavaScript, .sort() returns the sorted array. In Python, it does not. This difference in language behavior is similar to how reading properties of undefined works differently across languages.
Fix 3: Use Safe Dictionary Access
When you use dict.get(), it returns None for missing keys by default. If you immediately subscript that result, you get this error.
Broken code:
config = {"database": {"host": "localhost", "port": 5432}}
# "cache" key doesn't exist, .get() returns None
cache_host = config.get("cache")["host"] # TypeErrorFixed code — option A (check first):
cache_config = config.get("cache")
if cache_config is not None:
cache_host = cache_config["host"]
else:
cache_host = "default-host"Fixed code — option B (provide a default):
cache_host = config.get("cache", {}).get("host", "default-host")Option B chains .get() calls with empty dict defaults, so you never subscript None.
For deeply nested dictionaries, consider writing a helper:
def deep_get(data, *keys, default=None):
for key in keys:
if isinstance(data, dict):
data = data.get(key)
else:
return default
return data if data is not None else default
# Usage
host = deep_get(config, "cache", "host", default="localhost")This is much safer than chaining bracket access like config["cache"]["host"], which would raise a KeyError instead. Both errors — TypeError from None subscripting and KeyError from missing keys — point to the same underlying problem: accessing data without validating its structure first.
Fix 4: Handle API and JSON Responses
API responses frequently contain None (or null in JSON) for optional fields. If you subscript those fields without checking, you hit this error.
Broken code:
import requests
response = requests.get("https://api.example.com/user/42")
data = response.json()
# "address" might be null in the JSON response
street = data["address"]["street"] # TypeError if address is NoneFixed code:
import requests
response = requests.get("https://api.example.com/user/42")
data = response.json()
address = data.get("address")
if address is not None:
street = address.get("street", "Unknown")
else:
street = "Unknown"For complex API responses, validate the structure before accessing nested data:
def safe_get_address(data):
if not isinstance(data, dict):
return None
address = data.get("address")
if not isinstance(address, dict):
return None
return address.get("street")You can also use libraries like pydantic or marshmallow to validate API responses into typed models, which eliminates this class of error entirely. If your API integration uses modules that Python cannot find, check out how to fix Python ModuleNotFoundError.
Fix 5: Check Regex Match Before Subscripting
re.match() and re.search() return None when the pattern does not match the string. If you try to access groups on a None result, you get this error.
Broken code:
import re
text = "No numbers here"
match = re.search(r"(\d+)", text)
number = match.group(1) # AttributeError on None
# Or with [] syntax:
number = match[1] # TypeError: 'NoneType' object is not subscriptableSince Python 3.8, you can subscript match objects directly with match[1], which makes this TypeError appear more often in regex code.
Fixed code:
import re
text = "No numbers here"
match = re.search(r"(\d+)", text)
if match:
number = match[1]
else:
number = None # or a default valueUsing the walrus operator (Python 3.8+):
import re
text = "The price is 42 dollars"
if match := re.search(r"(\d+)", text):
number = int(match[1])
else:
number = 0The walrus operator (:=) assigns and checks in one line, keeping the code compact.
Fix 6: Avoid Chained Method Call Traps
Chaining methods is convenient, but if any method in the chain returns None, everything after it breaks.
Broken code:
# .lower() returns a string, but .replace() also returns a string,
# so this chain works fine:
text = "Hello World".lower().replace(" ", "_")
# But watch out for methods that return None:
data = [3, 1, 2]
first = data.sort()[0] # TypeError — .sort() returns NoneAnother common chained call trap with dictionaries:
user_data = {}
result = user_data.setdefault("preferences", {}).get("theme", {}).get("color")
# This works, but if you do:
result = user_data.pop("preferences", None)["theme"] # TypeError if pop returns NoneFix: Break the chain. Assign intermediate results to variables and check each one:
data = [3, 1, 2]
data.sort()
first = data[0]For longer chains, test each step:
raw = get_response() # Could return None
if raw is None:
raise ValueError("No response received")
parsed = raw.get("data") # Could return None
if parsed is None:
raise ValueError("Response missing 'data' field")
item = parsed[0] # Safe — we know parsed existsThis approach is more verbose but much easier to debug. When you hit an error, the traceback points to the exact step that failed. This is similar to how recursion depth errors require you to trace through the call chain to find the root cause.
Fix 7: Debug with Print and Type Checks
When you cannot immediately spot the source of None, add targeted debugging.
Print the value and its type before the failing line:
result = some_function()
print(f"result = {result}, type = {type(result)}") # Debug line
value = result[0] # Line that crashesThis tells you exactly what result holds. If it prints result = None, type = <class 'NoneType'>, you know the function returned None.
Use assert to catch None early:
result = some_function()
assert result is not None, "some_function() returned None"
value = result[0]This gives a clear error message instead of a confusing TypeError down the line.
Use conditional breakpoints in your debugger:
If you use VS Code or PyCharm, set a conditional breakpoint on the failing line with the condition result is None. The debugger will pause only when the variable is None, letting you inspect the call stack.
Common Mistake: Adding a
try/except TypeErroraround the subscript operation without fixing the root cause. This hides the bug and can lead to silent data corruption. Instead of catching the error, find out why the variable isNoneand fix the source. Exception handling should be a last resort for truly unpredictable cases, not a way to paper over logic errors.
Check function signatures for missing returns:
A systematic way to find the bug is to trace backwards from the error:
- Identify which variable is
Nonefrom the traceback. - Find where that variable gets its value (usually a function call or method).
- Read that function’s code — check every code path for a
returnstatement. - If any path lacks a
return, that is your bug.
This process works for most cases. For trickier issues involving indentation affecting control flow, the problem might be that a return statement is inside a block it shouldn’t be in — or outside a block where it should be.
Fix 8: Guard Against None in Class Attributes
Object attributes can be None if they are initialized as None and never set before being subscripted.
Broken code:
class Config:
def __init__(self):
self.settings = None
def load(self, data):
self.settings = data
config = Config()
# Forgot to call config.load()
print(config.settings["debug"]) # TypeErrorFixed code:
class Config:
def __init__(self):
self.settings = {}
def load(self, data):
if isinstance(data, dict):
self.settings = data
config = Config()
print(config.settings.get("debug", False)) # Safe — defaults to empty dictAlternatively, enforce that load() must be called before accessing settings:
class Config:
def __init__(self):
self._settings = None
def load(self, data):
self._settings = data
@property
def settings(self):
if self._settings is None:
raise RuntimeError("Config not loaded. Call .load() first.")
return self._settingsThis pattern — using a property to guard access — makes the error message explicit and actionable.
Fix 9: Handle None in List Comprehensions and Generators
When processing collections, individual elements might be None, causing the error inside a comprehension.
Broken code:
users = [
{"name": "Alice", "address": {"city": "NYC"}},
{"name": "Bob", "address": None},
{"name": "Charlie", "address": {"city": "LA"}},
]
cities = [user["address"]["city"] for user in users] # TypeError on BobFixed code:
cities = [
user["address"]["city"]
for user in users
if user.get("address") is not None
]Or if you need a default for missing addresses:
cities = [
user["address"]["city"] if user.get("address") else "Unknown"
for user in users
]For more complex filtering, extract the logic into a function:
def get_city(user):
address = user.get("address")
if address is None:
return "Unknown"
return address.get("city", "Unknown")
cities = [get_city(user) for user in users]Fix 10: Use Optional Type Hints to Prevent the Error
Python’s type system can catch NoneType subscript errors before they happen. This is a preventive fix.
from typing import Optional
def fetch_data(url: str) -> Optional[dict]:
"""Returns a dict or None if the request fails."""
response = requests.get(url)
if response.status_code == 200:
return response.json()
return NoneWith this annotation, tools like mypy will flag any code that subscripts the result without a None check:
data = fetch_data("https://api.example.com")
# mypy error: Item "None" of "Optional[dict]" has no attribute "__getitem__"
print(data["key"])mypy forces you to add a guard:
data = fetch_data("https://api.example.com")
if data is not None:
print(data["key"]) # No mypy errorRunning mypy --strict on your codebase catches most NoneType subscript errors at analysis time. If you are setting up Python tooling and run into issues with your Python installation not being found, resolve that first before configuring mypy.
Still Not Working?
If you have tried the fixes above and still get TypeError: 'NoneType' object is not subscriptable, try these less obvious solutions:
Check for variable shadowing. A common cause is accidentally reusing a variable name:
data = {"key": "value"}
# ... many lines later ...
data = some_function_that_returns_none()
print(data["key"]) # TypeError — data is now NoneSearch your file for all assignments to the variable name on the left side of the failing line. One of them is setting it to None.
Check for mutable default arguments. This classic Python gotcha can cause unexpected None values:
def process(items=None):
if items is None:
items = []
# ... process items
return items if items else None # Bug: returns None for empty listCheck for threading or async race conditions. In concurrent code, a variable might be None at the moment one thread reads it, even though another thread sets it shortly after. Use locks or thread-safe data structures to prevent this.
Check decorator behavior. If a function is wrapped by a decorator that does not properly return the function’s result, it will return None:
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Before call")
func(*args, **kwargs) # Missing: result = func(...); return result
print("After call")
return wrapper
@my_decorator
def get_data():
return {"key": "value"}
result = get_data()
print(result["key"]) # TypeError — decorator swallowed the return valueFix the decorator:
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Before call")
result = func(*args, **kwargs)
print("After call")
return result # Pass through the return value
return wrapperUse Python’s traceback module for more context. If the error happens deep in a call stack and the standard traceback is not enough, use:
import traceback
try:
value = result[0]
except TypeError:
traceback.print_exc()
print(f"Debug: result = {result!r}")
raiseThis prints the full stack trace plus the offending value, making it easier to trace the None back to its source.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: AWS Lambda Unable to import module / Runtime.ImportModuleError
How to fix the AWS Lambda Runtime.ImportModuleError and Unable to import module error caused by wrong handler paths, missing dependencies, layer issues, and packaging problems.
Fix: Python TypeError: unhashable type: 'list'
Learn why Python raises TypeError unhashable type list, dict, or set and how to fix it when using dictionary keys, sets, groupby, dataclasses, and custom classes.
Fix: Django Forbidden (403) CSRF verification failed
How to fix Django 403 CSRF verification failed error caused by missing CSRF tokens, AJAX requests, cross-origin issues, HTTPS misconfig, and session problems.
Fix: FastAPI 422 Unprocessable Entity (validation error)
How to fix FastAPI 422 Unprocessable Entity error caused by wrong request body format, missing fields, type mismatches, query parameter errors, and Pydantic validation.