sqlalchemy/sqlalchemy · error · ValueError

Value %r is not None, True, or False

Error message

Value %r is not None, True, or False

What it means

Raised by the Boolean type's _strict_as_bool() helper (sqltypes.py:2190) when a value bound or rendered as a literal for a Boolean column is an integer other than 0 or 1. The _strict_bools frozenset is {None, True, False}; because 0 == False and 1 == True those pass, but any other int (2, -1, 10) falls through to this ValueError. SQLAlchemy uses strict bool validation so that the values sent to a native-boolean or int-boolean column are unambiguous.

Source

Thrown at lib/sqlalchemy/sql/sqltypes.py:2195

                self._should_create_constraint,
                variant_mapping=variant_mapping,
            ),
            _type_bound=True,
        )
        assert e.table is table

    @property
    def python_type(self):
        return bool

    _strict_bools = frozenset([None, True, False])

    def _strict_as_bool(self, value):
        if value not in self._strict_bools:
            if not isinstance(value, int):
                raise TypeError("Not a boolean value: %r" % (value,))
            else:
                raise ValueError(
                    "Value %r is not None, True, or False" % (value,)
                )
        return value

    def literal_processor(self, dialect):
        compiler = dialect.statement_compiler(dialect, None)
        true = compiler.visit_true(None)
        false = compiler.visit_false(None)

        def process(value):
            return true if self._strict_as_bool(value) else false

        return process

    def bind_processor(self, dialect):
        _strict_as_bool = self._strict_as_bool

        _coerce: Union[Type[bool], Type[int]]

View on GitHub (pinned to d9b44cb731)

Solutions

  1. Coerce the offending value to a real bool before binding, e.g. bool(value) or an explicit map such as {0: False, 1: True}[value].
  2. If the column legitimately holds more than two states, change its type to Integer or an Enum instead of Boolean.
  3. Cleanse legacy/sentinel values at the source (UPDATE the 2/-1 rows to 0/1) so the data only contains valid booleans.
  4. For pandas/numpy inputs, cast the Series to bool dtype (.astype(bool)) before passing rows to insert().

Example fix

// before
session.execute(insert(User).values(active=2))

// after
session.execute(insert(User).values(active=bool(user_raw_flag)))
Defensive patterns

Strategy: validation

Validate before calling

def ensure_bool_value(v):
    if v not in (None, True, False):
        raise ValueError(
            "Boolean column requires None, True, or False; got %r" % (v,)
        )
    return v

# Call before binding to a Boolean column / parameter:
# session.execute(stmt, {"flag": ensure_bool_value(user_value)})

Type guard

def is_bool_compatible(v) -> bool:
    # Note: bool is a subclass of int, so check bool BEFORE int to avoid
    # silently accepting 0/1.
    return v is None or isinstance(v, bool)

Try / catch

from sqlalchemy import exc

try:
    session.execute(stmt, {"flag": user_value})
except (exc.StatementError, ValueError) as err:
    if "is not None, True, or False" in str(err):
        # value could not be coerced to Boolean; sanitize and retry
        sanitized = None if user_value is None else bool(user_value in (True, 1, "true", "1"))
        session.execute(stmt, {"flag": sanitized})
    else:
        raise

Prevention

When it happens

Trigger: Inserting/updating a Boolean column with an int value like 2, 3, or -1; building a literal_binds query with such an int against a Boolean column; passing numpy/other int-like objects whose equality with True/False fails inside the frozenset membership test. Note: floats (e.g. 1.0) or strings hit the separate TypeError path at sqltypes.py:2193, not this ValueError.

Common situations: Migrating data where a 'boolean' column actually stored 0/1/2 sentinel values; pandas/numpy int64 columns fed into ORM bulk inserts; test fixtures with integers representing enums coerced into Boolean fields; SQLite where booleans are stored as integers and legacy rows contain 2.

Related errors


AI-assisted analysis of sqlalchemy/sqlalchemy@d9b44cb731 (2026-08-01). Data as JSON: /data/errors/109882362605467e.json. Report an issue: GitHub.