mypy Statement is unreachable (tested)

mypy reports Statement is unreachable [unreachable] only when --warn-unreachable is on, for code its type narrowing says can never run: after an isinstance that always matches the declared type, after sys.exit(), or in a branch where a bool has no values left. Usually the annotation is too narrow.

1. Reproduce it

b1.py:

import sys

def parse(value: int) -> str:
    if isinstance(value, int):
        return "int"
    return "other"                  # value is declared int, so never reached

def check(flag: bool) -> None:
    if flag is True:
        print("yes")
    elif flag is False:
        print("no")
    else:
        print("impossible")         # bool narrowed away

def stop() -> None:
    sys.exit(1)
    print("after exit")             # sys.exit returns NoReturn

if sys.platform == "win32":
    import winreg                   # skipped on Linux, never reported

b2.py, the fixed version:

def parse(value: int | str) -> str:   # widen the type if other values really arrive
    if isinstance(value, int):
        return "int"
    return "other"

def check(flag: bool) -> None:
    if flag:
        print("yes")
    else:
        print("no")

def legacy(value: int) -> str:
    if isinstance(value, int):
        return "int"
    return "other"  # type: ignore[unreachable]

Output (mypy 2.4.0, Python 3.13.5, --no-incremental):

$ mypy b1.py
Success: no issues found in 1 source file

$ mypy --warn-unreachable b1.py
b1.py:6: error: Statement is unreachable  [unreachable]
b1.py:14: error: Statement is unreachable  [unreachable]
b1.py:18: error: Statement is unreachable  [unreachable]
Found 3 errors in 1 file (checked 1 source file)

$ mypy --warn-unreachable b2.py
Success: no issues found in 1 source file

2. Why each line is unreachable

3. Keeping a defensive branch

If you want a defensive fallback anyway, mark it with # type: ignore[unreachable] (legacy() in b2.py passed). For exhaustive checks on enums or unions, end with typing.assert_never(x) instead of an unreachable return.

Frequently asked questions

Does mypy --strict enable warn-unreachable?

No. In this test mypy --strict reported nothing; you need --warn-unreachable or warn_unreachable = true.

Why is my isinstance branch unreachable in mypy?

The parameter is annotated with a type that the isinstance check fully covers. Widen the annotation if other types really arrive.

How do I ignore a mypy unreachable error?

Add # type: ignore[unreachable] to the line.

Written with AI assistance. Measured on 2026-10-23 with mypy 2.4.0 and Python 3.13.5 on Linux. Files were checked verbatim and the output pasted unedited. PlainTables is our extension; mypy is free.