I'm trying to understand why an exception raised based on the type of a variable doesn't narrow down the type of this variable. I'd like to do something like this: def ensure_int(obj: int | str) -> None: if isinstance(obj, str): raise ValueError("obj cannot be str") def f(x: int | str) -> int: ensure_int(x) return x I would've thought that calling ensure_int in f would narrow the type of x down to int, but it doesn't. Mypy gives: error: Incompatible return value type (got "Union[int, str]", expected "int") [return-value] Why does this not work? If I inline the code of ensure_int into f then the error goes away. So, my questions are: Are there scenarios where calling ensure_int would not guarantee that the type of x is int? Is there a way of fixing this with additional annotations or similar? I read about TypeGuard and TypeIs but they only work with functions which return bool. Continue reading...