Bash & Python Error Guide: 'TypeError: NoneType object is not subscriptable'
Fix Python TypeError: 'NoneType' object is not subscriptable/iterable: functions returning None, dict.get misses, mutating methods, and unguarded API responses.
- #automation
- #troubleshooting
- #errors
- #python
Overview
TypeError: 'NoneType' object is not subscriptable (and its sibling ... is not iterable) means you tried to index, slice, or loop over a value that turned out to be None. Python’s None is a singleton with no __getitem__ and no __iter__, so value[0], value["key"], or for x in value: raises. The error is almost never about the indexing operation itself — it is a signal that something upstream produced None where you expected a list, dict, or string. Finding the bug means finding where that None came from.
The two common forms:
Traceback (most recent call last):
File "/srv/app/report.py", line 19, in <module>
name = record["name"]
~~~~~~^^^^^^^^
TypeError: 'NoneType' object is not subscriptable
Traceback (most recent call last):
File "/srv/app/report.py", line 24, in <module>
for item in get_items():
TypeError: 'NoneType' object is not iterable
It occurs at runtime, on the line that does the indexing/iteration. The frequent sources of the None: a function that falls off the end without return, dict.get() missing a key, a list/dict method that returns None (.sort(), .append()), an API/JSON field that was absent, or a regex match that did not match.
Symptoms
- A traceback ending in
'NoneType' object is not subscriptableornot iterable. - The failing line indexes (
x[...]) or iterates (for ... in x) a variable. - The code works for some inputs and fails for others (the
Noneonly appears on certain paths). - It worked against one data source/API and breaks against another that omits a field.
python3 report.py
TypeError: 'NoneType' object is not subscriptable
# Quick reproduction of the shape
python3 -c "x = None; print(x['k'])"
TypeError: 'NoneType' object is not subscriptable
Common Root Causes
1. A function returns None implicitly
A function that does work but forgets to return (or returns only on some branches) yields None, which the caller then indexes.
def find_user(uid):
for u in users:
if u["id"] == uid:
return u
# no return when not found -> None
u = find_user(99)
print(u["name"]) # u is None
TypeError: 'NoneType' object is not subscriptable
Return a default or handle the miss at the call site.
2. dict.get() returned None for a missing key, then you indexed it
config.get("db") returns None when "db" is absent; indexing [...] on that result fails.
config = {"app": {"name": "svc"}}
host = config.get("db")["host"]
TypeError: 'NoneType' object is not subscriptable
Provide a default or guard:
host = (config.get("db") or {}).get("host")
3. A mutating method returned None
list.sort(), list.append(), dict.update(), str.strip() chained wrong — many in-place methods return None, so assigning their result loses the data.
items = [3, 1, 2].sort() # sort() returns None
print(items[0])
TypeError: 'NoneType' object is not subscriptable
Use the non-mutating form (sorted(...)) or sort then use the original list:
items = sorted([3, 1, 2]); print(items[0])
4. A JSON/API response was missing the field
Parsed JSON lacked a key, so a nested access produced None, then a further index failed.
import json
data = json.loads('{"meta": {}}')
first = data["meta"]["items"][0] # "items" missing -> None
TypeError: 'NoneType' object is not subscriptable
Note: a literally-missing key raises KeyError; a key whose value is null ("items": null) gives None and this TypeError. Default the value:
items = (data.get("meta") or {}).get("items") or []
5. A regex match/search did not match
re.match(...) returns None on no match; calling .group(1) or indexing it raises.
import re
m = re.match(r"v(\d+)", "release-3")
print(m[1]) # no match -> m is None
TypeError: 'NoneType' object is not subscriptable
Check if m: before using it.
6. An environment variable or os.environ.get was unset
os.environ.get("LIST") returns None when unset; iterating or slicing it fails.
import os
for host in os.environ.get("HOSTS").split(","): # unset -> None.split fails
print(host)
AttributeError: 'NoneType' object has no attribute 'split'
(Here the same root cause surfaces as AttributeError.) Default it:
for host in (os.environ.get("HOSTS") or "").split(","):
...
Diagnostic Workflow
Step 1: Read the traceback to the exact failing expression
python3 report.py
The last frame shows the line and (on 3.11+) carets under the subexpression that was None.
Step 2: Print the suspect value right before the failing line
print("DEBUG record =", repr(record)) # is it None?
name = record["name"]
repr(...) == None confirms the value, not the indexing, is the problem.
Step 3: Trace where that value was produced
grep -n 'record\s*=' report.py
Look at the assignment — a function call, .get(), a method, or a parse — and ask whether it can yield None.
Step 4: Run under the debugger to inspect on failure
python3 -m pdb -c continue report.py
# at the post-mortem prompt:
# (Pdb) p record
# (Pdb) where
pdb drops you at the failing frame so you can inspect the None and its caller.
Step 5: Add a guard and re-run
record = lookup(key)
if record is None:
raise ValueError(f"no record for {key}") # or handle/default
name = record["name"]
Example Root Cause Analysis
A nightly job enriches alerts with metadata from an internal API. It works for most alerts but crashes on a few:
Traceback (most recent call last):
File "/srv/enrich/run.py", line 41, in enrich
owner = meta["owner"]["email"]
~~~~~~~~~~~~~~^^^^^^^^^
TypeError: 'NoneType' object is not subscriptable
Printing the value just before line 41:
print("DEBUG meta['owner'] =", repr(meta.get("owner")))
DEBUG meta['owner'] = None
The API returns {"owner": null} for services that have no assigned owner — a valid response, but the code assumed owner was always a dict. For owned services it is a dict and the index works; for unowned ones it is None and ["email"] fails. Because most services have owners, the bug only surfaced for the unowned minority.
Fix: treat a null owner as a normal case with a default:
owner = (meta.get("owner") or {}).get("email", "unassigned")
The job now processes unowned services without crashing.
Prevention Best Practices
- Make functions return a consistent type on every path; if “not found” is possible, return an explicit sentinel and check for it at the call site rather than letting an implicit
Noneflow downstream. - Guard external data (JSON, API responses, env vars) with
(value or default)or explicitif value is None:checks before indexing or iterating; remember that a JSONnullyieldsNone, not aKeyError. - Never assign the result of in-place methods (
.sort(),.append(),.update()); use the non-mutating equivalents (sorted,list + [x]) when you need a value. - Always test regex results (
if m:) before calling.group()or indexing the match. - Add type hints and run
mypy; it flags many “could be None here” paths before runtime. - For tracebacks that surface in scheduled automation, the free incident assistant can point to the upstream call that produced the
None. More patterns in the Bash & Python automation guides.
Quick Command Reference
# Reproduce the shape
python3 -c "x = None; print(x[0])"
# Run and read the full traceback
python3 report.py
# Post-mortem debugging at the failure point
python3 -m pdb -c continue report.py
# (Pdb) p <var> / (Pdb) where
# Find where the suspect value is assigned
grep -n '<var>\s*=' report.py
# Static check for None-related issues
mypy report.py
Conclusion
TypeError: 'NoneType' object is not subscriptable/iterable means you indexed or looped over a value that was None. The error is a downstream symptom; the bug is wherever the None originated. The recurring sources:
- A function returning
Noneimplicitly on some path. dict.get()missing a key, then being indexed.- An in-place method (
.sort(),.append()) whoseNonereturn was assigned. - A JSON/API field that was absent or explicitly
null. - A regex
match/searchthat did not match. - An unset environment variable read via
os.environ.get.
Print the suspect value with repr() right before the failing line, trace its assignment, then guard the producer with a default or an explicit is None check — fix the source, not just the index.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.