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
- 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].
- If the column legitimately holds more than two states, change its type to Integer or an Enum instead of Boolean.
- Cleanse legacy/sentinel values at the source (UPDATE the 2/-1 rows to 0/1) so the data only contains valid booleans.
- 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
- Only pass Python `bool` literals (True/False) or None to Boolean columns; never raw ints, strings like 'true'/'false', or 0/1.
- Because `bool` subclasses `int`, coerce inputs explicitly and check `isinstance(x, bool)` before `isinstance(x, int)`.
- Validate form/API input at the application boundary and convert to bool/None before reaching the ORM layer.
- For nullable booleans, distinguish 'unset' (None) from 'false' explicitly rather than relying on falsy coercion.
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
- BitString must only contain '0' and '1' chars
- value must be non-negative
- length must be non-negative
- Cannot encode {value} as a BitString of length {length}
- Cannot encode {value!r} as a BitString of length {length}
AI-assisted analysis of sqlalchemy/sqlalchemy@d9b44cb731 (2026-08-01).
Data as JSON: /data/errors/109882362605467e.json.
Report an issue: GitHub.