PythonMastery
reference 4 min read · lesson 44 of 45 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 into. 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

Four sources of None account for almost all of them:

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 falls off the end returns None — often 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: a new sorted list, original untouched
print(ranked[0])           # 72

Check the lookup before you index it. A regex that didn't match is normal, not exceptional — 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 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 actually 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 actually did, not just whether a test passed.