Skip to content
Commitpedia
Log in Join & edit

Fix: TypeError: ‘NoneType’ object is not subscriptable

You used [ ] on a value that is None. Something you expected to return a list, dict or match returned nothing. Find where the None came from, then handle it.

Edit history of Fix: TypeError: ‘NoneType’ object is not subscriptable: 1 recorded edit
Edit history · 1 edit
Type
Article
Edits
1
Editors
1
First issued
15 Oct 2026
Last edit
15 Oct 2026
Reading
2 min

The error looks like this:

Traceback (most recent call last):
  File "app.py", line 14, in <module>
    print(user["email"])
TypeError: 'NoneType' object is not subscriptable

“Subscriptable” means “supports square brackets”, like items[0] or data["key"]. None does not. So the variable on that line holds None instead of the list, dict or string you expected. The line in the traceback shows where it crashed; the real bug is wherever that None was produced.

Cause 1: a method that changes things in place

List methods such as sort(), append(), reverse() and dict update() modify the object and return None.

names = ["cy", "ana", "ben"]
sorted_names = names.sort()   # sorted_names is None
print(sorted_names[0])        # TypeError

# fix: use the function that returns a new list
sorted_names = sorted(names)
print(sorted_names[0])        # 'ana'

Cause 2: a function that sometimes returns nothing

A Python function without a return on some path returns None on that path.

def find_user(users, name):
    for user in users:
        if user["name"] == name:
            return user
    # falls through: returns None when nobody matches


user = find_user(users, "bob")
if user is None:
    print("No user named bob")
else:
    print(user["email"])

Decide what “not found” should mean. Either check for None at the call site as above, or make the function raise a clear exception (raise LookupError(f"no user {name!r}")) so the failure happens where the cause is.

Cause 3: dict.get() on a missing key

port = config.get("server")["port"]          # TypeError if "server" is missing

port = config.get("server", {}).get("port", 5432)  # default at each level

Cause 4: a regex that did not match

re.match and re.search return None when nothing matches.

import re

if (m := re.match(r"(\d+)-(\d+)", text)) is not None:
    start, end = m[1], m[2]
else:
    print(f"unexpected format: {text!r}")

Cause 5: data that contains null

JSON null becomes Python None. An API response like {"address": null} makes data["address"]["city"] fail. Check the field before going deeper.

How to find the source quickly

  1. Look at the failing line and list every [ ] on it; one of those values is None.
  2. Print or log them just before that line: print(f"{user=}").
  3. Follow the variable back to where it was assigned. Was it the result of an in-place method, a lookup, or a function with a missing return?
  4. Fix the source, or handle the None case explicitly with if x is None:.

Type hints help prevent this class of bug. Annotating def find_user(...) -> dict | None lets a type checker such as mypy or pyright warn you wherever the result is used without a None check. The same reasoning explains the sibling errors 'NoneType' object is not iterable and 'NoneType' object has no attribute ....

See also

  • 0007 Fix: ModuleNotFoundError: No module named ‘…’ Python cannot find a module you import. Usually the package was installed into a different interpreter or virtual environment than the one… Beginner · Fix · Any system Article Common errors · Environments & packages 15 Oct 2026
  • 0073 Fix: json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0) Python's json module found nothing it could parse at the very first character. The input is usually empty, HTML, or already consumed.… Beginner · Fix · Any system Article Common errors · CSV & JSON 15 Oct 2026

Repos in the wild

  • Ruff: Python linter and formatter astral-sh/ruff Ruff is a fast Python linter and code formatter written in Rust. It catches bugs such as undefined names and unused imports before you run your code. GitHub

History

Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.

  1. commitpedia-admin +3,013 chars

Report a problem

Something wrong, unsafe or copied? Tell the editors.
What is wrong?