Skip to content

possibly-undefined false positive when assigning variable for every case in match statement for all possible values #16992

Open

Description

@DetachHead
# mypy: enable-error-code=possibly-undefined

from typing import Literal

value: Literal[1, 2]

match value:
    case 1:
        result = 1
    case 2:
        result = 2

result # error: possibly-undefined

playground

similar issue: #15958 (seems it was only fixed when using assert_never at the end)

Activity

  1. Andrej730 commented on Jun 27, 2024

    @Andrej730

    Interesting that adding case _ kind of resolves the issue though mypy does detect that case _ code is unreachable.

    # mypy: enable-error-code=possibly-undefined
    
    from typing import Literal
    
    value: Literal[1, 2]
    
    match value:
        case 1:
            result = 1
        case 2:
            result = 2
        case _:
            # error: Statement is unreachable  [unreachable]
            result = 3
    
    result

    I've also met the similar issue with if isinstance checks. In the last case adding redudant else block makes the case false negative.

    # mypy: enable-error-code="possibly-undefined"
    
    class A: ...
    class B: ...
    
    # False positive: Name "new_name" may be undefined  [possibly-undefined]
    def test_1(obj: A | B) -> None:
        if isinstance(obj, A):
            new_name = "test"
        elif isinstance(obj, B):
            new_name = "test"
        a = new_name
    
    # + adding "else: reveal_type(obj)" somehow resolves it
    def test_1b(obj: A | B) -> None:
        if isinstance(obj, A):
            new_name = "test"
        elif isinstance(obj, B):
            new_name = "test"
        else:
            reveal_type(obj)
        a = new_name
    
    # False positive: Name "new_name" may be undefined  [possibly-undefined]
    def get_obj() -> A | B: ...
    def test_2() -> None:
        obj = get_obj()
        if isinstance(obj, A):
            new_name = "test"
        elif isinstance(obj, B):
            new_name = "test"
        a = new_name
    # but adding "else: reveal_type(obj)" somehow resolves it
    def test_2b() -> None:
        obj = get_obj()
        if isinstance(obj, A):
            new_name = "test"
        elif isinstance(obj, B):
            new_name = "test"
        else:
            reveal_type(obj)
        a = new_name
    
    # Name "new_name" may be undefined  [possibly-undefined]
    # and it's correct
    def test_3(obj: str | float) -> None:
        if isinstance(obj, str):
            new_name = "test"
        elif isinstance(obj, float):
            new_name = "test"
        a = new_name
    
    # but adding "else: reveal_type(obj)" makes it false negative
    def test_3b(obj: str | float) -> None:
        if isinstance(obj, str):
            new_name = "test"
        elif isinstance(obj, float):
            new_name = "test"
        else:
            reveal_type(obj)
        a = new_name
  2. tamird commented on May 1, 2026

    @tamird
    Contributor

    Another simple case that demonstrates the inconsistency with unreachable code:

    def takes_list_problem[T](stuff: list[T]) -> None:
        match stuff:
            case [one]:
                pass
            case [*_]:
                raise AssertionError("nope")
                
        print(one) # error: Name "one" may be undefined  [possibly-undefined]
        
    def takes_list_no_proble[T](stuff: list[T]) -> None:
        def one() -> T:
            match stuff:
                case [one]:
                    return one
                case [*_]:
                    raise AssertionError("nope")
        
        print(one())

    playground

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions