TypeError: 'type' object is not subscriptable (tested fix)

TypeError: 'type' object is not subscriptable usually means code with type hints like list[int] is running on Python 3.8 or older. We ran the same files on Python 3.8 and 3.13 and tested each fix.

1. list[int] in a type hint on Python 3.8 or older

The same file runs on Python 3.13 and crashes on 3.8. Annotations on a def are evaluated when the function is defined, so the error fires at import time, before you call anything:

import sys
print(sys.version.split()[0])
def total(xs: list[int]) -> int:
    return sum(xs)
print(total([1, 2, 3]))

Output:

$ python3.8 b1.py
3.8.20
Traceback (most recent call last):
  File "b1.py", line 3, in <module>
    def total(xs: list[int]) -> int:
TypeError: 'type' object is not subscriptable
$ python3.13 b1.py
3.13.5
6

Builtin generics like list[int] became valid at runtime in Python 3.9 (PEP 585). If your laptop runs 3.11 and a server, Docker base image or CI job runs 3.8, this is the first error you see.

2. Which builtins fail

import sys
print(sys.version.split()[0])
for expr in ["list[int]", "dict[str, int]", "tuple[int, ...]", "type[int]", "set[str]"]:
    try:
        print(expr, "->", eval(expr))
    except TypeError as e:
        print(expr, "->", type(e).__name__ + ":", e)

Output:

$ python3.8 b2.py
3.8.20
list[int] -> TypeError: 'type' object is not subscriptable
dict[str, int] -> TypeError: 'type' object is not subscriptable
tuple[int, ...] -> TypeError: 'type' object is not subscriptable
type[int] -> TypeError: 'type' object is not subscriptable
set[str] -> TypeError: 'type' object is not subscriptable
$ python3.13 b2.py
3.13.5
list[int] -> list[int]
dict[str, int] -> dict[str, int]
tuple[int, ...] -> tuple[int, ...]
type[int] -> type[int]
set[str] -> set[str]

Every builtin you subscript gives the same message on 3.8. It never names which type it was, so look at the line in the traceback.

3. Fix A: from __future__ import annotations

Put this as the first statement of the module. Annotations are then stored as strings and never evaluated:

from __future__ import annotations
import sys
print(sys.version.split()[0])
def total(xs: list[int]) -> int:
    return sum(xs)
print(total([1, 2, 3]))
print(total.__annotations__)

Output:

$ python3.8 b3.py
3.8.20
6
{'xs': 'list[int]', 'return': 'int'}

It only covers annotations. A type alias assigned at module level is ordinary code and still fails on 3.8:

from __future__ import annotations
import sys
print(sys.version.split()[0])
Scores = dict[str, int]   # alias at module level is evaluated, not an annotation
print(Scores)

Output:

$ python3.8 b4.py
3.8.20
Traceback (most recent call last):
  File "b4.py", line 4, in <module>
    Scores = dict[str, int]   # alias at module level is evaluated, not an annotation
TypeError: 'type' object is not subscriptable
$ python3.13 b4.py
3.13.5
dict[str, int]

Anything that evaluates the string annotations later, such as typing.get_type_hints() (which pydantic, dataclass tools and FastAPI rely on), raises the same error on 3.8:

from __future__ import annotations
import sys, typing
print(sys.version.split()[0])
def total(xs: list[int]) -> int:
    return sum(xs)
try:
    print(typing.get_type_hints(total))
except TypeError as e:
    print(type(e).__name__ + ":", e)

Output:

$ python3.8 b7.py
3.8.20
TypeError: 'type' object is not subscriptable
$ python3.13 b7.py
3.13.5
{'xs': list[int], 'return': <class 'int'>}

4. Fix B: use typing.List and typing.Dict

The typing versions work on every Python 3 version, for annotations and aliases alike:

import sys
from typing import Dict, List
print(sys.version.split()[0])
Scores = Dict[str, int]
def total(xs: List[int]) -> int:
    return sum(xs)
print(Scores, total([1, 2, 3]))

Output:

$ python3.8 b5.py
3.8.20
typing.Dict[str, int] 6

The third fix is to upgrade the interpreter to 3.9 or later. Python 3.8 reached end of life in October 2024, so this is usually the right long-term fix, and typing.List is just a bridge until then.

5. Indexing a class instead of an instance

The other common cause is writing Point[0] or cls["x"] when you meant an instance (a missing ()). Newer Pythons name the class in the message:

class Point:
    pass
try:
    Point[0]
except TypeError as e:
    print(type(e).__name__ + ":", e)
p = Point
try:
    p["x"]
except TypeError as e:
    print(type(e).__name__ + ":", e)

Output:

$ python3.8 b6.py
TypeError: 'type' object is not subscriptable
TypeError: 'type' object is not subscriptable
$ python3.13 b6.py
TypeError: type 'Point' is not subscriptable
TypeError: type 'Point' is not subscriptable

On 3.11 and 3.13 the message is type 'Point' is not subscriptable; on 3.8 it is the generic 'type' object is not subscriptable. Create the instance with Point(), or add __getitem__ (or __class_getitem__ for generic classes).

6. Related: isinstance with list[int]

try:
    isinstance([1, 2], list[int])
except TypeError as e:
    print(type(e).__name__ + ":", e)
print(isinstance([1, 2], list))

Output:

$ python3.13 b8.py
TypeError: isinstance() argument 2 cannot be a parameterized generic
True

Even on new versions, isinstance can't check element types. Check against plain list, then check the items yourself. Related: object is not subscriptable (other types), 'NoneType' object is not subscriptable and 'dict_keys' object is not subscriptable.

Frequently asked questions

Why does list[int] give 'type' object is not subscriptable?

Builtin generics like list[int] and dict[str, int] only work at runtime from Python 3.9. On 3.8 add from __future__ import annotations, use typing.List, or upgrade.

Does from __future__ import annotations fix it everywhere?

Only in annotations. Module-level aliases like Scores = dict[str, int] and typing.get_type_hints() still raise on 3.8.

Why does my class give type 'X' is not subscriptable?

You indexed the class, not an instance. Usually a missing () when creating it.

Written with AI assistance. Measured on 2026-10-22 on Debian with Python 3.13.5 and in the official python:3.8-alpine (3.8.20) and python:3.11-alpine Docker images: builtin generics in annotations and aliases, from __future__ import annotations, typing.get_type_hints, typing.List/Dict, indexing a class, and isinstance with list[int]. Every block was run verbatim and its output pasted unedited. PlainHeaders is our extension; Python is free.