Skip to content

Narrowing on a missing attribute narrows it to object instead of Unknown, causing cascading errors #5333

Description

@QEDady

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

https://pyrefly.org/sandbox/?project=v2.rVa7UsMwEPyVm3FhUySiVstQQ0MVMkjBiuNhkJhYAfvvWT2MnTgKYHAp3d36tHsr_dYyz5nNd2Fe1EXLwReMq-MuJMq519sovmiXW2OugsSyuKNKh4JheNAv2nxoKm453YXBMFsKgy1QX9AOBGpD0tp9vTlYRQLlBK1e6waKrxZfG2uoJE6Oh-S9ruOX0T9gjMdp1GKqPVSn4in8TXSNRIV4SJMKIWtI8oGr6_WksxvphAbEUrUO15AIuehjI8uF3_C2eYrdzSbIUe78Yh5LHvdnLM0BOn_QXarRQcQnjGEVewne-rNL8BZyh1QfPp-9qUdldB8WKI47bmeYW3XAGwCmtXzUGa5_RTtr3xrOWJ9u9hVTmpXmuWFHGYycHct3WfuXxXE5Mt5TG5T9iznmwRvzS9aYO2dExEW3-wQ

(Only applicable for extension issues) IDE Information

No response

Activity

  1. added
    googleissues from google
    narrowingIssues with narrowing - root cause is usually narrowing, flow handling, or both
    on Oct 22, 2026
  2. added
    needs-discussionAn issue where it's not clear whether there is a bug or we are behaving as expected.
    on Oct 23, 2026
  3. tonyyuyiding commented on Oct 23, 2026

    @tonyyuyiding
    Contributor

    I agree that x.foo should be Unknown here. I think inferring it as object does not provide any useful information.

  4. maharshi-coding commented on Nov 3, 2026

    @maharshi-coding

    #claim

  5. github-actions commented on Nov 3, 2026

    @github-actions

    @maharshi-coding you've claimed this issue — it's now assigned to you. Thanks for picking it up! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

googleissues from googlehas-open-prnarrowingIssues with narrowing - root cause is usually narrowing, flow handling, or bothneeds-discussionAn issue where it's not clear whether there is a bug or we are behaving as expected.typechecking

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions