sqlalchemy/sqlalchemy · error · UnevaluatableError
Cannot evaluate column: {clause}
Error message
Cannot evaluate column: {clause} What it means
Raised by visit_column when a Column object inside the WHERE clause lacks the 'parentmapper' annotation, meaning SQLAlchemy cannot tell which ORM mapper (and therefore which attribute on the target objects) the column corresponds to. The evaluator needs that mapping to turn the column reference into an attribute lookup on each in-memory object. This typically happens when the WHERE clause uses a raw/literal column or a column from a Core select that was never associated with the ORM mapper being updated/deleted. With synchronize_session='auto' it triggers a silent fallback to 'fetch'.
Source
Thrown at lib/sqlalchemy/orm/evaluator.py:91
return meth(clause)
def visit_grouping(self, clause):
return self.process(clause.element)
def visit_null(self, clause):
return lambda obj: None
def visit_false(self, clause):
return lambda obj: False
def visit_true(self, clause):
return lambda obj: True
def visit_column(self, clause):
try:
parentmapper = clause._annotations["parentmapper"]
except KeyError as ke:
raise UnevaluatableError(
f"Cannot evaluate column: {clause}"
) from ke
if self.target_cls and not issubclass(
self.target_cls, parentmapper.class_
):
raise UnevaluatableError(
"Can't evaluate criteria against "
f"alternate class {parentmapper.class_}"
)
parentmapper._check_configure()
# we'd like to use "proxy_key" annotation to get the "key", however
# in relationship primaryjoin cases proxy_key is sometimes deannotated
# and sometimes apparently not present in the first place (?).
# While I can stop it from being deannotated (though need to see if
# this breaks other things), not sure right now about cases where it'sView on GitHub (pinned to d9b44cb731)
Solutions
- Reference the mapped attribute (e.g. User.name) rather than the raw column object (User.__table__.c.name or literal_column).
- Pass synchronize_session='fetch' (or False) so the predicate is not evaluated in Python: session.execute(stmt, execution_options={'synchronize_session': 'fetch'}).
- If using a CTE/subquery, ensure the column is referenced through the annotated ORM entity (stmt.c.col aliased to the mapped attribute) so parentmapper is available.
Example fix
# before
session.execute(
update(User).where(User.__table__.c.name == "alice")
)
# after
session.execute(
update(User).where(User.name == "alice")
) Defensive patterns
Strategy: validation
Validate before calling
from sqlalchemy.orm.exc import UnevaluatableError
def uses_annotated_column(clause) -> bool:
"""False if a column in the criteria lacks a 'parentmapper'
annotation (raw Table column, CTE column, label, etc.)."""
for col in clause.get_children() if hasattr(clause, 'get_children') else []:
if hasattr(col, '_annotations') and 'parentmapper' not in col._annotations:
return False
return True
# Better: just build the WHERE from mapped class attributes.
# query.filter(User.name == 'x') # OK (User.name carries parentmapper)
# query.filter(user_table.c.name == 'x') # risk: raw column, no annotation Type guard
null
Try / catch
from sqlalchemy.orm.exc import UnevaluatableError
try:
query.update(values, synchronize_session='evaluate')
except UnevaluatableError:
query.update(values, synchronize_session='fetch') Prevention
- Build bulk-update/delete WHERE clauses from mapped class attributes (Model.col), never from raw Table.c columns, select/CTE labels, or text() fragments.
- If you must use a column-like construct that is not bound to a mapper, pass synchronize_session='fetch' or False.
When it happens
Trigger: Using update(Model).where(literal_column('col_name') == value) or referencing a standalone Column() object (not the mapped attribute) in a bulk DML WHERE clause, e.g. update(User).where(user_table.c.name == 'x') where user_table.c.name is the raw table column rather than the mapped User.name attribute. Also triggered by column literals produced by with_only_columns() or text() that bypass ORM annotation.
Common situations: Writing ORM bulk updates against table-level Column objects (Model.__table__.c.x) instead of the mapped attributes; using literal_column() or text() in the predicate; building a query from a Core select()/update() that was wrapped/converted into an ORM-executed statement losing annotations.
Related errors
- Cannot evaluate {type(clause).__name__}
- Can't evaluate criteria against alternate class {parentmappe
- Cannot evaluate expression: {err}
- Cannot evaluate clauselist with operator {clause.operator}
- Cannot evaluate {type(clause).__name__} with operator {claus
AI-assisted analysis of sqlalchemy/sqlalchemy@d9b44cb731 (2026-08-01).
Data as JSON: /data/errors/46758ee185d68ae2.json.
Report an issue: GitHub.