Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Bash & Python Automation By James Joyner IV · · 9 min read

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 subscriptable or not iterable.
  • The failing line indexes (x[...]) or iterates (for ... in x) a variable.
  • The code works for some inputs and fails for others (the None only 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 None flow downstream.
  • Guard external data (JSON, API responses, env vars) with (value or default) or explicit if value is None: checks before indexing or iterating; remember that a JSON null yields None, not a KeyError.
  • 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:

  1. A function returning None implicitly on some path.
  2. dict.get() missing a key, then being indexed.
  3. An in-place method (.sort(), .append()) whose None return was assigned.
  4. A JSON/API field that was absent or explicitly null.
  5. A regex match/search that did not match.
  6. 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.

Free download · 368-page PDF

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.