"TypeError: 'NoneType' object is not subscriptable" means you used square brackets, as in x[0], x["name"] or x[:4], on a variable that holds None. Python can't index or slice None. To fix it, find the expression the traceback underlines, work out which function or lookup handed you None, and either make it return real data or check if x is not None before you index it.

In practice the None nearly always comes from one of a few places. A function forgot to return, you saved the result of list.sort(), a lookup such as dict.get(), re.match() or fetchone() found nothing, or an API sent back null. Python's docs describe TypeError as the exception for an operation applied to an object of the wrong type, and that's exactly what's happening here.

I reproduced every example in this guide myself. Each broken script ran on Python 3.11.17, 3.13.5, 3.14.8 and 3.15.0rc3, and all four printed the same traceback and message. The type-checker examples used mypy 2.4.1 and Pyright 1.1.414, and the HTML example used Beautiful Soup 4.15.0.

In this guide:

  • What 'not subscriptable' actually means
  • Read the traceback first: the carets point at the None
  • 1. A function that forgets to return
  • 2. Saving the result of list.sort(), append() or another in-place method
  • 3. dict.get() returned None for a missing key
  • 4. re.match() or re.search() found nothing
  • 5. A JSON null or an API field that is None
  • 6. A variable that starts as None and never gets replaced
  • 7. cursor.fetchone() returned no row
  • 8. A function that only returns on some paths
  • 9. BeautifulSoup find() did not match
  • 10. os.environ.get() for a variable that is not set
  • Writing to None: 'does not support item assignment'
  • Catch it before it runs: type hints with mypy and Pyright
  • A quick debugging checklist
  • Which fix should you use?
  • Frequently asked questions

What 'not subscriptable' actually means

"Subscript" is Python's word for the square-bracket operation. A list, tuple, string or dict lets you subscript it because its type defines a __getitem__ method, which the data model reference covers. None doesn't have one, so None[0] fails straight away.

The same None value causes a family of closely related errors, depending on what you tried to do with it. I ran each operation against x = None on all four Python versions:

Message What you wrote Same root cause?
'NoneType' object is not subscriptable x[0], x["key"], x[:3] Yes, x is None
'NoneType' object does not support item assignment x["key"] = 1 Yes, but you were writing, not reading
'NoneType' object has no attribute ... x.name, x.group(1) Yes, you used a dot instead of brackets
'NoneType' object is not iterable for i in x, a, b = x Yes, you looped over or unpacked it
'int' object is not subscriptable 5[0] No, the value is a number, not None

If you came here from the attribute or iterable version of this error, the causes below apply to you too.

Read the traceback first: the carets point at the None

Since Python 3.11, tracebacks underline the exact part of the line that failed. The ~ characters mark the value that turned out to be None, and the ^ characters mark the subscript you tried to apply to it. That's a big help when one line chains several calls together:

 print(find_product(products, "B2")["price"]) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^ TypeError: 'NoneType' object is not subscriptable 

Here the tildes sit under find_product(products, "B2"), so that call returned None. The ["price"] part is fine. Once you know which expression is None, the cause is usually one of the ten below.

1. A function that forgets to return

This is the most common cause by far. A Python function without a return statement still returns something: None. The function below builds the right dict, prints from it, then throws it away:

def get_user(user_id): user = {"id": user_id, "name": "Ana"} print("loaded", user["name"]) # forgot: return user  user = get_user(1) print(user["name"]) 
loaded Ana Traceback (most recent call last): File "/workspace/seo-posts-nov04/test/python/c01_noreturn.py", line 6, in <module> print(user["name"]) ~~~~^^^^^^^^ TypeError: 'NoneType' object is not subscriptable 

The "loaded Ana" line shows the function ran and had the data, so the problem is what it handed back. Fix: return the value.

def get_user(user_id): user = {"id": user_id, "name": "Ana"} return user 
Ana 

Watch out for print() standing in for return, especially in code you wrote while experimenting in the REPL. Printing shows the value on screen, but the caller still gets None.

2. Saving the result of list.sort(), append() or another in-place method

Methods that change a list in place return None on purpose, as a reminder that they didn't create a new list. Assign that result back to your variable and you've replaced your list with None:

scores = [42, 7, 19] scores = scores.sort() print(scores[0]) 
 print(scores[0]) ~~~~~~^^^ TypeError: 'NoneType' object is not subscriptable 

I checked the usual suspects. Every one of these returns None:

list.append() returns None list.extend() returns None list.insert() returns None list.sort() returns None list.reverse() returns None list.clear() returns None dict.update() returns None random.shuffle() returns None 

Fix: call the method on its own line, or use sorted(), which returns a new list and leaves the original alone:

scores = [42, 7, 19] scores.sort() print(scores[0]) prices = [42, 7, 19] cheapest_first = sorted(prices) print(cheapest_first[0], prices) 
7 7 [42, 7, 19] 

The Python docs on built-in types note that sort() modifies the list in place and doesn't return the sorted sequence, for exactly this reason.

3. dict.get() returned None for a missing key

dict.get(key) is popular because it doesn't raise KeyError. The catch is that it returns None when the key is missing, so chaining another lookup onto it moves the crash one step to the right:

config = {"debug": True} host = config.get("database")["host"] 
 host = config.get("database")["host"] ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^ TypeError: 'NoneType' object is not subscriptable 

Fix: give get() a default of the right type, or check before you go deeper:

host = config.get("database", {}).get("host", "localhost") print("host:", host) db = config.get("database") if db is not None: print(db["host"]) else: print("no database section in config") 
host: localhost no database section in config 

Use the empty-dict default when a missing section is normal, and the explicit check when it's a real problem worth reporting. If you're still choosing between dicts, lists and tuples for data like this, see my comparison of Python list vs tuple vs set vs dict.

4. re.match() or re.search() found nothing

Regex functions return a match object when they succeed and None when they don't. The classic mistake is using re.match(), which only matches at the start of the string, when the text you want is in the middle:

import re line = "Order total: 45 GBP" m = re.match(r"(\d+) GBP", line) print(m) print(m[1]) 
None print(m[1]) ~^^^ TypeError: 'NoneType' object is not subscriptable 

If you write m.group(1) instead of m[1], you get the attribute version: 'NoneType' object has no attribute 'group'. Same cause.

Fix: use re.search() to find the pattern anywhere in the string, and always check the result. The walrus operator keeps it short:

print("match: ", re.match(r"(\d+) GBP", line)) print("search:", re.search(r"(\d+) GBP", line)) if (m := re.search(r"(\d+) GBP", line)): print("amount =", m[1]) else: print("no amount found") 
match: None search: <re.Match object; span=(13, 19), match='45 GBP'> amount = 45 

The re module docs have a short section on search() vs match() that's worth reading once.

5. A JSON null or an API field that is None

JSON's null becomes Python's None when you parse it. APIs often send null for "no value yet", such as a guest checkout with no user. The key exists, so data["user"] works, but the value inside it is None:

payload = '{"user": null, "items": []}' data = json.loads(payload) print(data) print(data["user"]["email"]) 
{'user': None, 'items': []} print(data["user"]["email"]) ~~~~~~~~~~~~^^^^^^^^^ TypeError: 'NoneType' object is not subscriptable 

Notice that get("user", {}) would not help here, because the key is present. The default only applies to missing keys. Fix: use or {}, which also replaces an explicit None:

data = json.loads('{"user": null, "items": []}') user = data.get("user") or {} print("email:", user.get("email", "not set")) 
email: not set 

Printing the parsed data is the quickest way to spot a hidden None. For saving that data back out in readable form, see how to write JSON to a file with pretty printing.

6. A variable that starts as None and never gets replaced

Starting a variable as None and filling it inside a loop works until the loop runs zero times, say on an empty file or an empty API page:

best = None for row in []: # e.g. an empty API page or empty file  best = row print(best["score"]) 
 print(best["score"]) ~~~~^^^^^^^^^ TypeError: 'NoneType' object is not subscriptable 

Fix: handle the empty case on purpose. For "find the biggest" logic, max() with a default makes the empty case explicit:

rows = [] best = max(rows, key=lambda r: r["score"], default=None) if best is None: print("no rows yet") else: print(best["score"]) 
no rows yet 

The built-in functions page documents the default argument for both max() and min().

7. cursor.fetchone() returned no row

Database cursors return None from fetchone() when the query matched nothing. That's easy to forget when you're looking up a single record by ID:

row = con.execute("SELECT email FROM users WHERE id = ?", (99,)).fetchone() print(row) print(row[0]) 
None print(row[0]) ~~~^^^ TypeError: 'NoneType' object is not subscriptable 

Fix: treat "not found" as a normal result and check for it:

for uid in (1, 99): row = con.execute("SELECT email FROM users WHERE id = ?", (uid,)).fetchone() if row is None: print(f"user {uid}: not found") else: print(f"user {uid}: {row[0]}") 
user 1: ana@example.com user 99: not found 

I used the built-in sqlite3 module, but MySQL and PostgreSQL drivers behave the same way. fetchall(), by contrast, gives you an empty list.

8. A function that only returns on some paths

This one is sneakier than cause 1, because the function does return, just not every time. With no match, Python falls off the end and returns None:

def find_product(products, sku): for p in products: if p["sku"] == sku: return p products = [{"sku": "A1", "price": 9.99}] print(find_product(products, "A1")["price"]) print(find_product(products, "B2")["price"]) 
9.99 print(find_product(products, "B2")["price"]) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^ TypeError: 'NoneType' object is not subscriptable 

Tests that only try the happy path will pass. Fix: decide what "not found" should do. Either return None on purpose and make callers check for it, or raise a clear error:

def find_product(products, sku): for p in products: if p["sku"] == sku: return p raise LookupError(f"no product with sku {sku!r}") 
LookupError: no product with sku 'B2' 

A LookupError that names the missing SKU tells you far more than a TypeError three lines later. JavaScript has the same trap, where it shows up as Cannot read properties of undefined.

9. BeautifulSoup find() did not match

If you scrape HTML, you'll meet this one often. Beautiful Soup's find() returns None when no tag matches, so a small typo in a class name, or a site redesign, breaks a line like this:

html = '<div><a class="btn" href="/signup">Sign up</a></div>' soup = BeautifulSoup(html, "html.parser") print(soup.find("a", class_="btn")["href"]) print(soup.find("a", class_="button")["href"]) 
/signup print(soup.find("a", class_="button")["href"]) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^ TypeError: 'NoneType' object is not subscriptable 

Fix: store the tag, check it, and use .get() for attributes that might be missing:

link = soup.find("a", class_="button") print(link["href"] if link is not None else "link not found") print(soup.select_one("a.btn").get("href")) 
link not found /signup 

select_one() also returns None when nothing matches, so give it the same check in real code. The Beautiful Soup documentation covers both methods.

10. os.environ.get() for a variable that is not set

Secrets and settings often come from environment variables. os.environ.get() returns None when the variable isn't set, which is common on a new machine, in a fresh terminal, or in a CI job that doesn't have the secret:

secret = os.environ.get("API_SECRET") print("first 4 chars:", secret[:4]) 
 print("first 4 chars:", secret[:4]) ~~~~~~^^^^ TypeError: 'NoneType' object is not subscriptable 

Slicing counts as subscripting too, which is why [:4] triggers the same error. Fix: fail early with a message a human can act on:

secret = os.environ.get("API_SECRET") if not secret: raise SystemExit("API_SECRET is not set. Export it before running this script.") print("first 4 chars:", secret[:4]) 
API_SECRET is not set. Export it before running this script. first 4 chars: sk_t 

The first line is the run with the variable unset, and the second is the run with API_SECRET=sk_test_123. If the variable is required, os.environ["API_SECRET"] is also fine, because it raises a KeyError that names the missing variable. The os module docs cover both forms.

Writing to None: 'does not support item assignment'

Use brackets on the left of an equals sign and the message changes slightly, but the cause stays the same:

cart = None cart["apples"] = 3 
 cart["apples"] = 3 ~~~~^^^^^^^^^^ TypeError: 'NoneType' object does not support item assignment 

The fix is to create the container before you fill it, such as cart = {}, or to find out why the function you expected to return a dict returned None. My free tools page has more small coding helpers.

Catch it before it runs: type hints with mypy and Pyright

If a function can return None, say so in its type hint. A type checker will then flag every place where you index the result without checking it first:

from typing import Optional def load_settings(path: str) -> Optional[dict[str, str]]: if path.endswith("Source:https://dev.to/mfaysaldev/typeerror-nonetype-object-is-not-subscriptable-in-python-10-causes-and-fixes-tested-examples-5n9

Related posts