Python enum ValueError: is not a valid (tested)

ValueError: 'X' is not a valid MyEnum means MyEnum(x) found no member with that value. We measured the common causes on Python 3.13.5: looking up by name, strings instead of numbers, and case.

1. Status(x) looks up by value, Status[x] by name

from enum import Enum
class Status(Enum):
    ACTIVE = 1
    BLOCKED = 2
for v in [1, "1", "ACTIVE", 3]:
    try:
        print(repr(v), "->", Status(v))
    except ValueError as e:
        print(repr(v), "-> ValueError:", e)
print("by name:", Status["ACTIVE"])
try:
    Status["active"]
except KeyError as e:
    print("Status['active'] -> KeyError:", e)

Output:

1 -> Status.ACTIVE
'1' -> ValueError: '1' is not a valid Status
'ACTIVE' -> ValueError: 'ACTIVE' is not a valid Status
3 -> ValueError: 3 is not a valid Status
by name: Status.ACTIVE
Status['active'] -> KeyError: 'active'

Measured on Python 3.13.5: calling the enum class, Status(v), matches against member values with no type conversion, so "1" doesn't match 1 and the member name "ACTIVE" doesn't match either. Looking up by name uses square brackets, and a bad name is a KeyError, not a ValueError. Both lookups are case-sensitive.

2. Numbers that arrive as strings

import json
from enum import Enum
class Status(Enum):
    ACTIVE = 1
    BLOCKED = 2
row = json.loads('{"status": "2"}')          # many APIs and CSVs send numbers as strings
try:
    Status(row["status"])
except ValueError as e:
    print("ValueError:", e)
print(Status(int(row["status"])))

Output:

ValueError: '2' is not a valid Status
Status.BLOCKED

Measured: the error message quotes the value ('2'), which is the clue that a string arrived where the enum has an integer. Convert at the boundary where the data comes in: Status(int(row["status"])).

3. Accept variants with _missing_

from enum import Enum, StrEnum
class Color(StrEnum):
    RED = "red"
    GREEN = "green"
    @classmethod
    def _missing_(cls, value):               # called only when the plain lookup fails
        if isinstance(value, str):
            for member in cls:
                if member.value == value.strip().lower():
                    return member
        return None                          # -> the normal ValueError
for v in ["red", " Green ", "RED", "blue"]:
    try:
        print(repr(v), "->", repr(Color(v)))
    except ValueError as e:
        print(repr(v), "-> ValueError:", e)

Output:

'red' -> <Color.RED: 'red'>
' Green ' -> <Color.GREEN: 'green'>
'RED' -> <Color.RED: 'red'>
'blue' -> ValueError: 'blue' is not a valid Color

Measured: _missing_ is called only after the normal lookup fails; returning a member accepts the value, returning None gives the usual ValueError. Here it makes lookups case- and whitespace-insensitive while "blue" is still rejected. For enum values that silently become aliases, see our duplicate enum value guide.

Frequently asked questions

What does ValueError: X is not a valid Enum mean?

No member has that value. We measured Status('1'), Status('ACTIVE') and Status(3) all raising it on Python 3.13.5 for an enum with values 1 and 2.

How do I get an enum member by name in Python?

Use square brackets: Status['ACTIVE']. We measured that, and a wrong name raising KeyError instead of ValueError.

Why does my enum fail with a number from JSON or CSV?

The number arrived as a string. We measured Status('2') failing and Status(int('2')) returning Status.BLOCKED.

How do I make enum lookup case-insensitive?

Override the _missing_ classmethod. We measured a StrEnum accepting 'RED' and ' Green ' while still rejecting 'blue'.

Written with AI assistance. Measured on 2026-10-09 with Python 3.13.5: Enum lookups by value and name with ints, numeric strings and names, a JSON string value, and a StrEnum with _missing_. Every block was run verbatim and its output pasted from that run. PlainHeaders is our extension; everything else here is built-in Python.