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's

View on GitHub (pinned to d9b44cb731)

Solutions

  1. Reference the mapped attribute (e.g. User.name) rather than the raw column object (User.__table__.c.name or literal_column).
  2. Pass synchronize_session='fetch' (or False) so the predicate is not evaluated in Python: session.execute(stmt, execution_options={'synchronize_session': 'fetch'}).
  3. 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

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


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