PythonerrorsTypeErrorJSON

list indices must be integers or slices, not str — Fix

TypeError: list indices must be integers or slices, not str means you indexed a list with a string key. The usual cause is JSON: parsing returned a list of dictionaries, so data['name'] has to become data[0]['name']. Inside: the one-line diagnosis, the mirror error string indices must be integers, and how it differs from KeyError and IndexError.

8 min readReferencePython · errors · TypeError · JSON · lists

TypeError: list indices must be integers or slices, not str means you indexed a list with a string key, the way you would index a dictionary. A list only accepts an integer or a slice inside the brackets — "name" is not a valid index.

Nearly always this happens while parsing JSON: json.loads() or response.json() returned a list of dictionaries, so data["name"] has to become data[0]["name"]. The message even names the culprit: the word list says the outer object is a list, not a dict.

What does "list indices must be integers or slices, not str" mean?

Python is telling you that the object to the left of the square brackets is a list, while the thing inside the brackets is a string. A list stores items by position, so you can only reach in with a number (data[0], data[-1]) or a slice (data[:3]). String keys belong to dictionaries, which are built differently. The error fires immediately, before Python ever looks for a field with that name.

import json

raw = '[{"name": "Maria", "score": 91}, {"name": "Igor", "score": 78}]'
data = json.loads(raw)

print(data["name"])
Traceback (most recent call last):
  File "app.py", line 6, in <module>
    print(data["name"])
          ~~~~^^^^^^^^
TypeError: list indices must be integers or slices, not str

The most useful part of the message is the first word. list indices means the object in front of the brackets is a list; a string would give string indices, a tuple tuple indices. Python has already named the real type of your data — you only have to believe it.

Why does JSON hand you a list of dictionaries?

The top level of a JSON document is either an object {...} or an array [...]: an object becomes a Python dict, an array becomes a list. API docs usually print one element, while the server returns an array of them — and that is where intuition breaks. The path copied from the docs, ["items"]["title"], needs one more level between the two keys: the element number.

response = {
    "status": "ok",
    "items": [
        {"id": 17, "title": "Coffee grinder", "price": 3990},
        {"id": 24, "title": "Travel mug", "price": 1290},
    ],
}

print(response["items"]["title"])
Traceback (most recent call last):
  File "app.py", line 9, in <module>
    print(response["items"]["title"])
          ~~~~~~~~~~~~~~~~~^^^^^^^^^
TypeError: list indices must be integers or slices, not str

response["items"] is already a list, so the next step has to be a number or a loop:

response = {
    "status": "ok",
    "items": [{"title": "Coffee grinder", "price": 3990}, {"title": "Travel mug", "price": 1290}],
}

print(response["items"][0]["title"])
print(sum(item["price"] for item in response["items"]))
Coffee grinder
5280

The same idea as a table — what actually sits in the variable, and the access that works.

What you have Broken Correct
List of dicts [{...}, {...}] data["name"] data[0]["name"]
Dict with a list inside {"items": [...]} data["items"]["title"] data["items"][0]["title"]
String holding JSON '[{"name": ...}]' data[0]["name"] json.loads(data)[0]["name"]
Row from csv.reader ["17", "Coffee grinder"] row["title"] row[1] or csv.DictReader

CSV sets the same trap: csv.reader yields lists of strings, not dictionaries, so row["title"] raises exactly this error. Switch to csv.DictReader — it reads the header row and turns every line into a dict, after which row["title"] works.

How do you check what you actually have in one line?

Stop guessing at the structure and print it. Three lines settle the question: type(data).__name__ gives the type, len(data) gives the size, and list(data)[:5] shows the first chunk without flooding the terminal with a megabyte of output. Reach for list(data)[:5] rather than a data[:1] slice: slicing a dict raises, while that one line is safe on a list and on a dict alike. More reliable than printing data whole: in a large response you cannot see the shape by eye. One precondition: the print has to land somewhere you can read it. Started by double-click, the window closes before you see either the output or the traceback — the fix is a way of launching the file, covered in the console window closes immediately.

import json

raw = '{"count": 2, "results": [{"name": "Maria"}, {"name": "Igor"}]}'
data = json.loads(raw)

print(type(data).__name__)
print(list(data)[:5])

items = data["results"]
print(type(items).__name__, len(items))
print(items[0])
dict
['count', 'results']
list 2
{'name': 'Maria'}

Now the shape is visible: a dict outside with keys count and results, and a list of two dicts inside results. The path to a name is data["results"][0]["name"]. When an endpoint returns a list sometimes and a dict other times, branch on isinstance(payload, list) instead of hoping — see type checks with isinstance.

How is this different from "string indices must be integers"?

They are neighbouring messages about the same confusion, but with different causes. list indices means the JSON was parsed and you are holding a list — an integer index is missing. string indices must be integers, not 'str' means nothing was parsed at all: the variable still holds the raw JSON text, and Python is dutifully trying to take a character out of it by position. The fixes differ: add an index in the first case, call json.loads() in the second.

text = '[{"name": "Maria", "score": 91}]'   # what f.read() or response.text gives you

print(text[0]["name"])
Traceback (most recent call last):
  File "app.py", line 3, in <module>
    print(text[0]["name"])
          ~~~~~~~^^^^^^^^
TypeError: string indices must be integers, not 'str'

text[0] returned the single character [, and then a key was requested from that character. Replace f.read() with json.load(f) and it becomes a real list of dicts. The same trap hits requests users: response.text is a string, the parsed structure comes from response.json().

The other common source of string indices is looping over a dictionary: for user in data yields keys, which are strings, so user["name"] blows up. Use data.values() or data.items().

What can go inside the brackets of a list?

An integer, a negative integer, or a slice — that is the complete list. Nothing else is accepted, neither a string nor a float.

prices = [199, 349, 990, 1290]
print(prices[0], prices[-1], prices[1:3])
199 1290 [349, 990]

A float index produces almost the same message, with a different type at the end:

prices = [199, 349, 990, 1290]
print(prices[len(prices) / 2])
Traceback (most recent call last):
  File "app.py", line 2, in <module>
    print(prices[len(prices) / 2])
          ~~~~~~^^^^^^^^^^^^^^^^^
TypeError: list indices must be integers or slices, not float

/ always produces a float in Python, even on an exact division. For the middle of a list use //: prices[len(prices) // 2] returns 990. Every slicing form is in the handbook under list slicing.

The mirror-image case — a list used as a dict key — raises TypeError: unhashable type: 'list', explained in TypeError: unhashable type 'list' in Python.

How do you loop over a list of dicts and read a field?

Loop with for: on each pass the loop variable is a whole dictionary you can index by key, so no positions are counted by hand. For one field from every element, a list comprehension is shorter; for a single element matching a condition, next() over a generator with a default does it in one line.

data = [{"name": "Maria", "score": 91}, {"name": "Igor", "score": 78}]

for user in data:
    print(user["name"], user["score"])

names = [user["name"] for user in data]
print(names)

igor = next((u for u in data if u["name"] == "Igor"), None)
print(igor)
Maria 91
Igor 78
['Maria', 'Igor']
{'name': 'Igor', 'score': 78}

Drill exactly this on the free task Cart total with a discount: the input is a list of item dictionaries to sum and discount. Watch out for the neighbouring bug — deleting elements inside such a loop silently skips some of them, as shown in deleting from a list while looping skips items.

TypeError vs KeyError vs IndexError

The three look similar but answer different questions. TypeError means the index has the wrong type: a string can never index a list. KeyError means the object really is a dict, but that key is missing. IndexError means it really is a list, but shorter than the position you asked for.

Message What happened First move
TypeError: list indices must be integers or slices, not str a list indexed with a string add a position: data[0]["name"]
TypeError: string indices must be integers, not 'str' JSON not parsed yet, still a string json.loads(text) or response.json()
KeyError: 'email' it is a dict, but the key is missing inspect list(user) or use user.get("email")
IndexError: list index out of range it is a list, but it is shorter check len(data)

Forgiving dictionary access (get, defaults, setdefault) is in dict access and defaults.

Common mistakes

  1. Copying a path from the API docs without checking the response shape. The docs show one object; the server returns an array of them. Fix: print(type(data).__name__, len(data)) before the first field access.
  2. Using response.text instead of response.json(). You keep a string, so you get string indices must be integers. Fix: response.json() or json.loads(response.text).
  3. Reading a file with f.read() instead of json.load(f). Same string-instead-of-structure problem.
  4. Looping over a dict and expecting values. for user in data gives string keys. Fix: data.values() or data.items().
  5. Indexing a list with whatever came out of input(). It is always a string. Fix: prices[int(n)].
  6. Computing the middle of a list with /. True division yields a float. Fix: use //.
  7. Reading a csv.reader row by column name. That reader yields lists. Fix: csv.DictReader.

Practice: take a nested JSON apart in the browser

This sticks only once you run code. On Python Arena tasks are graded by hidden tests in the browser, with nothing to install. Start with Merge similar items: you walk a list and combine matching entries — the exact operation where this error shows up. Then take the free dictionary block: dictionary tasks.

Reading and writing JSON safely, json.dump and json.load included, is covered under JSON serialization. And if saving a file turned non-ASCII letters into escape sequences, nothing is broken — see Python JSON: Cyrillic saved as Unicode escapes.

In short

  • list indices must be integers… means a string sits in the brackets of a list, where a position or slice belongs.
  • The first word is the diagnosis: list indices means you hold a list, string indices means you still hold a string.
  • JSON is the usual source: a top-level array becomes a list of dicts, so data["name"] becomes data[0]["name"].
  • Print type(data).__name__, len(data) and list(data)[:5] before the first field access.
  • string indices must be integers, not 'str' is a missing json.loads(), not a missing index.
  • Loop a list of dicts with for: the loop variable is already a dict and takes keys.
  • TypeError is a wrong index type, KeyError a missing key, IndexError a list that is too short.

Practice on real tasks

Solve tasks in the Python trainer with instant grading and hints.

Open trainer