mypy Incompatible types in assignment (tested)
mypy reports Incompatible types in assignment (expression has type "str", variable has type "int") when you assign a value whose type does not match the type the variable already has, usually inferred from its first assignment. Declare the wider type up front (x: int | str = 0) or narrow the value first.
1. The four common causes
b1.py:
from typing import Optional
count = 0
count = "zero" # int variable, str value
def find(name: str) -> Optional[int]:
return None if name == "" else len(name)
n: int = find("abc") # Optional[int] into int
prices = [1, 2, 3]
prices = [1.5, 2.5] # list[int] variable, list[float] value
total = 0
total += 0.5 # int += float
Output (mypy 2.4.0, Python 3.13.5):
$ mypy --no-incremental b1.py
b1.py:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]
b1.py:9: error: Incompatible types in assignment (expression has type "int | None", variable has type "int") [assignment]
b1.py:12: error: List item 0 has incompatible type "float"; expected "int" [list-item]
b1.py:12: error: List item 1 has incompatible type "float"; expected "int" [list-item]
b1.py:15: error: Incompatible types in assignment (expression has type "float", variable has type "int") [assignment]
Found 5 errors in 1 file (checked 1 source file)
- mypy fixes a variable's type at its first assignment.
count = 0makes itint, so a later"zero"is an error. - A function returning
Optional[int]cannot go straight into anintvariable: the message says"int | None". - Reassigning a
list[int]variable with floats is reported per item as[list-item], not[assignment]. total = 0thentotal += 0.5is the same error: the variable isint.
2. The fixes
from typing import Optional
count: int | str = 0
count = "zero"
def find(name: str) -> Optional[int]:
return None if name == "" else len(name)
found = find("abc")
n: int = found if found is not None else 0
prices: list[float] = [1, 2, 3]
prices = [1.5, 2.5]
total = 0.0
total += 0.5
$ mypy --no-incremental b2.py
Success: no issues found in 1 source file
Declare the wider type up front (int | str, list[float]), start from the right type (0.0), or narrow the Optional with an is not None check before assigning.
3. Default None and reusing a name
def load(path: str = None) -> str:
return path or "default.txt"
def parse(raw):
value = raw.strip()
value = int(value) # redefine with a new type
return value
def parse2(raw: str) -> int:
value = raw.strip()
value = int(value)
return value
$ mypy --no-incremental b3.py
b3.py:1: error: Incompatible default for parameter "path" (default has type "None", parameter has type "str") [assignment]
b3.py:1: note: PEP 484 prohibits implicit Optional. Accordingly, mypy has changed its default to no_implicit_optional=True
b3.py:1: note: Use https://github.com/hauntsaninja/no_implicit_optional to automatically upgrade your codebase
b3.py:11: error: Incompatible types in assignment (expression has type "int", variable has type "str") [assignment]
b3.py:12: error: Incompatible return value type (got "str", expected "int") [return-value]
Found 3 errors in 1 file (checked 1 source file)
$ mypy --no-incremental --allow-redefinition b3.py
b3.py:1: error: Incompatible default for parameter "path" (default has type "None", parameter has type "str") [assignment]
b3.py:1: note: PEP 484 prohibits implicit Optional. Accordingly, mypy has changed its default to no_implicit_optional=True
b3.py:1: note: Use https://github.com/hauntsaninja/no_implicit_optional to automatically upgrade your codebase
Found 1 error in 1 file (checked 1 source file)
path: str = Noneis reported as Incompatible default for parameter (also codeassignment). Writepath: str | None = None.parse()has no annotations, so mypy does not check its body at all by default; the identical code in annotatedparse2()is flagged.--allow-redefinitionlets a variable be re-bound with a different type in the same block (bothparse2errors went away). It does not fix theNonedefault.
To silence one line, use # type: ignore[assignment]; prefer fixing the type.
Frequently asked questions
Why does mypy say incompatible types in assignment?
The variable got a type at its first assignment (or annotation) and the new value has a different type, e.g. count = 0 then count = "zero".
How do I fix expression has type "int | None", variable has type "int"?
Check for None before assigning (x if x is not None else 0) or annotate the variable as int | None.
What does --allow-redefinition do?
It lets a variable be re-bound with a different type within the same block, e.g. value = raw.strip() then value = int(value).
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 with --no-incremental and the output pasted unedited. PlainTables is our extension; mypy is free.