Repository navigation
Description
Describe the Bug
When accessing a missing attribute, normal attribute lookup emits a [missing-attribute] error once and evaluates the expression to Unknown, preventing cascading errors on downstream uses.
However, when that attribute access appears in a condition that triggers narrowing (e.g., if x.foo:, if x.foo is not None:, if len(x.foo) == 3:, or in the else branch), the missing attribute is narrowed to object (or object | ...), causing cascading errors on subsequent uses of x.foo.
Repro
from typing import Any, reveal_type
def f(x: int, y: Any | None):
reveal_type(x.foo) # revealed type: Unknown (E: Object of class `int` has no attribute `foo` [missing-attribute])
if x.foo: # E: Object of class `int` has no attribute `foo` [missing-attribute]
reveal_type(x) # revealed type: int (_.foo: object)
reveal_type(x.foo) # revealed type: object
x.foo[0] # E: Cannot index into `object` [bad-index]
reveal_type(y.foo) # revealed type: Unknown (E: Object of class `NoneType` has no attribute `foo` [missing-attribute])
if y.foo: # E: Object of class `NoneType` has no attribute `foo` [missing-attribute]
reveal_type(y) # revealed type: Any | None (_.foo: object | Any)
reveal_type(y.foo) # revealed type: object | Any
y.foo[0] # E: Cannot index into `object` [bad-index]Would you agree that both x.foo and y.foo should be inferred as Unknown and the errors on x.Foo[0] and y.foo[0] to go away? Other type checker seems to not error on the access but keep repeating the missing-attribute error regardless, which I think is not ideal as well.
Sandbox Link
(Only applicable for extension issues) IDE Information
No response
Activity
tonyyuyiding commentedon Oct 23, 2026
I agree that
x.fooshould beUnknownhere. I think inferring it asobjectdoes not provide any useful information.maharshi-coding commentedon Nov 3, 2026
#claim
github-actions commentedon Nov 3, 2026
@maharshi-coding you've claimed this issue — it's now assigned to you. Thanks for picking it up! 🎉