boto/boto3 · error · DynamoDBOperationNotSupportedError

AND operation cannot be applied to value {value} of type {ty

Error message

AND operation cannot be applied to value {value} of type {type(value)} directly. Must use AttributeBase object methods (i.e. Attr().eq()). to generate ConditionBase instances first.

What it means

Raised by ConditionBase.__and__ when the & operator is applied between a ConditionBase (e.g. the result of Attr('x').eq(1)) and an operand that is NOT a ConditionBase. DynamoDB condition expressions can only be combined (&) with other conditions; combining a condition with a raw value, string, dict, or number is invalid because the library cannot build a valid DynamoDB FilterExpression/ConditionExpression from a non-condition.

Source

Thrown at boto3/dynamodb/conditions.py:35

    DynamoDBNeedsConditionError,
    DynamoDBNeedsKeyConditionError,
    DynamoDBOperationNotSupportedError,
)

ATTR_NAME_REGEX = re.compile(r'[^.\[\]]+(?![^\[]*\])')


class ConditionBase:
    expression_format = ''
    expression_operator = ''
    has_grouped_values = False

    def __init__(self, *values):
        self._values = values

    def __and__(self, other):
        if not isinstance(other, ConditionBase):
            raise DynamoDBOperationNotSupportedError('AND', other)
        return And(self, other)

    def __or__(self, other):
        if not isinstance(other, ConditionBase):
            raise DynamoDBOperationNotSupportedError('OR', other)
        return Or(self, other)

    def __invert__(self):
        return Not(self)

    def get_expression(self):
        return {
            'format': self.expression_format,
            'operator': self.expression_operator,
            'values': self._values,
        }

    def __eq__(self, other):

View on GitHub (pinned to c7b4afac23)

Solutions

  1. Ensure BOTH operands of & are ConditionBase instances, e.g. Attr('status').eq('active') & Attr('pending').eq(True).
  2. Check the right-hand operand with isinstance(other, ConditionBase) before combining.
  3. Chain conditions incrementally and assert each intermediate result is a condition.

Example fix

# before
fe = Attr('status').eq('active') & 'pending'
table.scan(FilterExpression=fe)

# after
fe = Attr('status').eq('active') & Attr('status').eq('pending')
table.scan(FilterExpression=fe)
Defensive patterns

Strategy: type-guard

Validate before calling

from boto3.dynamodb.conditions import ConditionBase

def safe_and(left, right):
    if not isinstance(right, ConditionBase):
        raise TypeError(f'Right operand of & must be a ConditionBase, got {type(right)}')
    return left & right

Type guard

from boto3.dynamodb.conditions import ConditionBase

def is_condition(v) -> bool:
    return isinstance(v, ConditionBase)

Try / catch

from boto3.exceptions import DynamoDBOperationNotSupportedError
try:
    expr = cond_a & other
except DynamoDBOperationNotSupportedError:
    # other was not a condition; build a proper condition instead
    expr = cond_a & Attr('field').eq(other)

Prevention

When it happens

Trigger: table.scan(FilterExpression=Attr('status').eq('active') & 'pending') — the right operand 'pending' is a str, not a ConditionBase. Also Attr('n').eq(1) & 2 or Attr('n').eq(1) & {'x': 1}.

Common situations: Forgetting to call .eq()/.gt()/.begins_with() on the second attribute operand; accidentally short-circuiting with a literal; copy-paste where one side of the & was truncated.

Related errors


AI-assisted analysis of boto/boto3@c7b4afac23 (2026-08-04). Data as JSON: /data/errors/1c013aeff2251ee0.json. Report an issue: GitHub.