Home/Blog/Python

// Python

TypeError: 'NoneType' object is not subscriptable, and the fix

Every example on this page was run on 12 October 2026 in Python 3.14, and the output under each one is what it printed. How we test these posts.

TypeError: 'NoneType' object is not subscriptable means you put square brackets after a value that is None, as in value[0] or value["key"]. Something you expected to be a list, a dict or a string came back as None, which has no items to look up. The usual fix is to find where that None came from, most often a function that does not return on every path or items = items.sort(), and keep the real value instead.

What does ‘NoneType’ object is not subscriptable mean?

'NoneType' object is not subscriptable means the value before the brackets is None. “Subscriptable” is Python’s word for a value you can index with [], and None is not one. The line in the traceback is where it broke, but the bug is usually earlier, wherever that None came from:

def find_user(name):
    users = {"ana": {"age": 31}}
    if name in users:
        return users[name]

user = find_user("bob")
print(user["age"])

Output

Traceback (most recent call last):
  File "main.py", line 7, in <module>
    print(user["age"])
          ~~~~^^^^^^^
TypeError: 'NoneType' object is not subscriptable

The carets point at user["age"], so user is the value that is None. The question is why.

Why is my variable None?

A variable is None when the code that should have given it a value returned nothing. Five places account for nearly every case.

A function that does not return on every path

A Python function that ends without a return gives back None. In find_user above, the return only runs when the name is found. For "bob" the function falls off the end, and user is None. The same happens when a function prints its result instead of returning it.

A recursive function that drops its own result

A recursive function returns None when one branch calls itself but forgets to return the call. The innermost call has the right answer, and the outer one throws it away:

def split_digits(n, digits):
    if n < 10:
        return [n] + digits
    split_digits(n // 10, [n % 10] + digits)

print(split_digits(472, [])[0])

Output

Traceback (most recent call last):
  File "main.py", line 6, in <module>
    print(split_digits(472, [])[0])
          ~~~~~~~~~~~~~~~~~~~~~^^^
TypeError: 'NoneType' object is not subscriptable

Writing return split_digits(n // 10, [n % 10] + digits) on line 4 fixes it, and split_digits(472, [])[0] then gives 4.

After sort(): a list method that changes the list in place

sort(), append(), reverse(), extend() and insert() change the list and return None. Assigning their result back throws the list away:

scores = [60, 90, 75]
scores = scores.sort()
print(scores[0])

Output

Traceback (most recent call last):
  File "main.py", line 3, in <module>
    print(scores[0])
          ~~~~~~^^^
TypeError: 'NoneType' object is not subscriptable

The Python FAQ explains why list.sort() does not return the sorted list: returning None is a reminder that the original list was changed.

A lookup that found nothing

dict.get() returns None for a missing key, and re.match() and re.search() return None when the pattern does not match. Chaining brackets straight onto them works until the day the data is different:

import re
m = re.search(r"(\d+)", "no digits here")
print(m[1])

Output

Traceback (most recent call last):
  File "main.py", line 3, in <module>
    print(m[1])
          ~^^^
TypeError: 'NoneType' object is not subscriptable

Library calls do the same. BeautifulSoup’s find() returns None when no tag matches, a database cursor’s fetchone() returns None when there are no more rows, and OpenCV’s cv2.imread() returns None for a path it cannot read.

A placeholder that was never filled in

A variable set to None as a placeholder stays None when the code meant to fill it sits inside an if that did not run.

Why does pandas give ‘NoneType’ object is not subscriptable?

pandas gives 'NoneType' object is not subscriptable when you assign the result of a method called with inplace=True. With inplace=True, sort_values(), dropna(), fillna() and the rest change the DataFrame and return None, exactly like list.sort():

import pandas as pd
df = pd.DataFrame({"name": ["Liam", "Ava"], "score": [75, 90]})
df = df.sort_values("score", ascending=False, inplace=True)
print(df["name"])

Output

Traceback (most recent call last):
  File "main.py", line 4, in <module>
    print(df["name"])
          ~~^^^^^^^^
TypeError: 'NoneType' object is not subscriptable

Pick one style: assign without inplace, or use inplace=True without assigning. The sort_values reference shows both:

import pandas as pd
df = pd.DataFrame({"name": ["Liam", "Ava"], "score": [75, 90]})
df = df.sort_values("score", ascending=False)
print(df["name"].tolist())

Output

['Ava', 'Liam']

How do I fix ‘NoneType’ object is not subscriptable?

Fix 'NoneType' object is not subscriptable where the None was made, not where it was indexed.

Return something on every path, and check for None where a missing result is normal:

def find_user(name):
    users = {"ana": {"age": 31}}
    if name in users:
        return users[name]
    return None

for name in ["ana", "bob"]:
    user = find_user(name)
    if user is None:
        print(name, "not found")
    else:
        print(name, user["age"])

Output

ana 31
bob not found

Writing return None yourself changes nothing for Python, but it tells the next reader that None is a real answer they have to handle. If a missing user is a bug rather than a normal case, raise an error there instead, so the traceback points at the cause and not at a line three functions later.

Do not assign the result of an in-place method. Call it on its own line, or use the version that returns a new list:

scores = [60, 90, 75]
scores.sort()
print(scores[0])

top = sorted([60, 90, 75], reverse=True)
print(top[0])

Output

60
90

Check a lookup before you index it, or give get() a default:

import re
m = re.search(r"(\d+)", "order 42 shipped")
if m:
    print(m[1])
else:
    print("no number")

settings = {"theme": "dark"}
print(settings.get("fonts", ["sans"])[0])

Output

42
sans

settings.get("fonts", ["sans"]) returns the default list when the key is missing, so the [0] always has something to read.

How do I find which value is None?

Find the None by printing each step of the failing line with repr, so None cannot hide. When the line has several brackets, such as data["items"][0]["name"], the carets mark the part that failed, and the print shows which value was empty:

data = {"items": None}
print(repr(data["items"]))

Output

None

Then follow that value back to the line that set it. It will be one of the causes above.

How can mypy or pyright catch it before I run the code?

mypy and pyright find most of these without running anything. Give your functions return types, for example def find_user(name: str) -> dict | None:, and the checker reports any place that indexes the result without checking for None first. Both also know that sort() returns None, and both report the scores = scores.sort() mistake in your editor, on the assignment or on the line that indexes the result. The mypy page on optional types shows how X | None works.

TypeError: 'NoneType' object is not iterable is the same None in a for loop or an unpacking such as a, b = value, and AttributeError: 'NoneType' object has no attribute is the same None before a dot. An index past the end of a real list is a different error, IndexError: list index out of range, and JavaScript’s version of a missing value is TypeError: Cannot read properties of undefined.