PythonMastery
reference 4 min read · lesson 44 of 60 in Errors

TypeError: 'NoneType' object is not subscriptable

1 · The lesson

read

What this error means

You used square brackets (x[0], x["name"], x[1:3]) on a value that is None. None holds nothing, so there's nothing to index. The line that fails is rarely the line that's wrong: the real bug is wherever that None came from, usually a function that returned nothing when you expected a list, a dict or a match.

When you see it

text
Traceback (most recent call last):
  File "report.py", line 4, in <module>
    top = scores[0]
          ~~~~~~^^^
TypeError: 'NoneType' object is not subscriptable

Why it happens

Almost every case comes from one of these four sources of None:

1. A method that changes things in place. list.sort(), list.append(), list.reverse(), dict.update() and random.shuffle() all modify the object and return None. Assigning the result throws your data away.
2. Your own function has a path with no return. A function that reaches its end without a return returns None. Often this happens only for the one input you didn't test.
3. A lookup found nothing. dict.get(key) returns None for a missing key. re.match() and re.search() return None when the pattern doesn't match.
4. The data said null. A JSON field that's null arrives in Python as None.

python
scores = [72, 95, 88]
scores = scores.sort()     # sorts in place, returns None
print(scores[0])           # TypeError: 'NoneType' object is not subscriptable

How to fix it

Don't assign the result of an in-place method. Either call it on its own line, or use the version that returns a new object:

python
scores = [72, 95, 88]
scores.sort()              # modifies scores; nothing to assign
ranked = sorted(scores)    # or: make a new sorted list and leave the original as it is
print(ranked[0])           # 72

Check the result of the lookup before you index it. A regex that doesn't match is a normal case, so handle it:

python
import re

line = "ERROR disk full"
m = re.match(r"(\w+) (.*)", line)
if m is None:
    raise ValueError(f"unrecognised log line: {line!r}")
level, message = m[1], m[2]

Give .get() a default of the right type. If you're going to index the result, the fallback should be indexable too:

python
user = {"name": "Grace"}
first_tag = user.get("tags", [""])[0]    # "" rather than a crash

Make every path in your function return something. Read it from top to bottom and ask "what does this return when the if is false?" That branch is where the None comes from.

When you'd see this in real code

  • rows = rows.sort(key=...) in a report script, and every later rows[i] fails.
  • A scraper where soup.find("h1") found nothing, and the next line indexes the result.
  • response.json()["data"]["items"] where the API returned "data": null for an empty result.
  • os.environ.get("CONFIG")[0:5] on a machine where the variable was never set.

See Also

Practice this

on practicepython.in

Short exercises that run in your browser and tell you what your code did, as well as whether a test passed.

Found this useful? Share it with a friend who’s learning Python.