Pyright Type Incompatibility Error in Python
Align static type annotations using Union / |, refine function argument types with isinstance() type narrowing, or apply # type: ignore for intentional dynamic patterns.
Root Cause Analysis
This error occurs when a static type checker (such as Pyright, Pylance in VS Code, or Mypy) analyzes Python source code and detects that the inferred type of an expression does not satisfy the type constraints declared by the receiving parameter or variable annotation.
1. Static Type Checking vs Dynamic Runtime Execution
Python is dynamically typed at runtime, but modern IDEs use static analyzers like Pyright to catch bugs before execution. When a function annotates a parameter as int and callers pass a string (or str | int), Pyright flags Type 'str' cannot be assigned to type 'int'.
2. Invariant Container Types (list[Derived] vs list[Base])
In Python typing, mutable containers like list and dict are invariant. Passing a list[int] to a function expecting list[float] fails type checking even though integers are numerically compatible with floats.
3. Missing None Checks (Optional Types)
When a variable has type Optional[str] (or str | None), calling string methods like .lower() without an if val is not None: guard causes Pyright to flag Cannot access attribute 'lower' for type 'None'.
4. Overloaded Function Signatures
Complex functions returning different types depending on input flags require @overload decorators for Pyright to resolve return types precisely.
Reproduction Code (MCVE)
def format_account_id(account_id: int) -> str:
if not isinstance(account_id, int):
raise TypeError(f'Type incompatibility error: expected int, got {type(account_id).__name__}')
return f'ACC-{account_id:06d}'
# Passing str argument violates type annotation
format_account_id('98765')
Solution 1: Broaden Annotations with Union or Type Narrowing
Use int | str union types in Python 3.10+ and apply isinstance() checks to narrow types safely.
def format_account_id(account_id: int | str) -> str:
if isinstance(account_id, str):
account_id = int(account_id)
return f'ACC-{account_id:06d}'
print(format_account_id(12345))
print(format_account_id('67890'))
Solution 2: Use Sequence for Covariant Read-Only Containers
Annotate function parameters with collections.abc.Sequence or Iterable instead of list to allow subtype compatibility.
from collections.abc import Sequence
def calculate_total(values: Sequence[float]) -> float:
return sum(values)
int_list: list[int] = [10, 20, 30]
print(f'Total: {calculate_total(int_list)}')
A common mistake is using Any everywhere to silence type errors. While Any suppresses Pyright warnings, it disables all type safety benefits throughout downstream code. Prefer object for truly unknown inputs combined with isinstance() checks, or TypeVar for generic functions. Edge cases occur with Pydantic models: use pydantic.BaseModel fields rather than raw dictionary lookups to ensure automatic type validation. Contrast this static warning with runtime TypeError: unhashable type, which occurs when using mutable objects as dictionary keys.