Skip to content

[1.20 regression] Type narrowing is order sensitive in match-case statement with narrowed enum type #21187

Closed

Description

@SebastiaanZ

Bug Report

Under specific circumstances, changing the order of case-statements in a match-statement influences mypy's type narrowing, leading to flaky assert_never errors.

Specifically, this happens you narrow enum values to specific enum values using Literal and combine that narrowed type with other types in a union.

(I've not done an exhaustive search, but I've only seen this with Literal[SomeEnum.SPECIFIC_VALUE].)

This bug does not occur with mypy==1.19

I've tried to reproduce this bug with Mypy 1.19, but it only occurs with Mypy 1.20. (We ran into this after upgrading Mypy.)

To Reproduce

In the script below, the only different between the dummy_class_then_enum and enum_then_dummy_class is the order of the case-statements within the match statement.

In both instances, all possible value types are handled, but mypy will throw an error on the assert_never in the enum_then_dummy_class function.

import enum
from typing import Literal, assert_never


class DummyClass:
    """A dummy class."""


class MyEnum(enum.StrEnum):
    """A dummy enum class."""

    RELEVANT = enum.auto()
    IGNORED = enum.auto()


type MyUnionType = DummyClass | Literal[MyEnum.RELEVANT]


def dummy_class_then_enum(arg: MyUnionType) -> str:
    match arg:
        case DummyClass():
            return "dummy class"
        case MyEnum.RELEVANT:
            return "relevant"
        case _ as unreachable:
            assert_never(unreachable)


def enum_then_dummy_class(arg: MyUnionType) -> str:
    match arg:
        case MyEnum.RELEVANT:
            return "relevant"
        case DummyClass():
            return "dummy class"
        case _ as unreachable:
            assert_never(unreachable)

Expected Behavior

Mypy reports no errors (or errors for both functions).

Actual Behavior

We only get an error for the second function, not the first.

(mypy-demo) sebastiaan@python MypyDemo % mypy bug_demo.py
bug_demo.py:36: error: Argument 1 to "assert_never" has incompatible type "Literal[MyEnum.RELEVANT]"; expected "Never"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.20
  • Mypy command-line flags: None
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: 3.14.3

Activity

  1. changed the title [-][1.20 regression] Type narrowing is sometime order sensitive in match-case statement[/-] [+][1.20 regression] Type narrowing is order sensitive in match-case statement with narrowed enum type[/+] on Apr 8, 2026
  2. hauntsaninja commented on Apr 9, 2026

    @hauntsaninja
    Collaborator

    Thanks, bisects to #20146

  3. hauntsaninja commented on Apr 13, 2026

    @hauntsaninja
    Collaborator

    That commit made mypy's match narrowing use the same code paths as its equality narrowing (which is a correct thing to do). However, mypy's equality narrowing is a little overly conservative when it comes to StrEnum and IntEnum, and that existing deficiency is what is now exposed here

  4. added a commit that references this issue on Apr 24, 2026
    1e96fb7
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