boto/boto3 · error · DynamoDBOperationNotSupportedError

OR operation cannot be applied to value {value} of type {typ

Error message

OR 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.__or__ when the | operator is applied between a ConditionBase and an operand that is not a ConditionBase. Same principle as the AND variant: OR in DynamoDB condition expressions requires both sides to be conditions so the library can emit a valid OR node in the expression tree.

Source

Thrown at boto3/dynamodb/conditions.py:40

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):
        if isinstance(other, type(self)):
            if self._values == other._values:
                return True
        return False

View on GitHub (pinned to c7b4afac23)

Solutions

  1. Make the right operand a ConditionBase: Attr('status').eq('active') | Attr('status').eq('archived').
  2. Validate with isinstance(right, ConditionBase) before |.
  3. Use Attr(...).is_in([...]) when OR-ing equality on the same attribute.

Example fix

# before
fe = Attr('status').eq('active') | 'archived'

# after
fe = Attr('status').eq('active') | Attr('status').eq('archived')
# or equivalently
fe = Attr('status').is_in(['active', 'archived'])
Defensive patterns

Strategy: type-guard

Validate before calling

from boto3.dynamodb.conditions import ConditionBase

def safe_or(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:
    expr = cond_a | Attr('field').eq(other)

Prevention

When it happens

Trigger: table.scan(FilterExpression=Attr('status').eq('active') | 'archived') — the right operand is a bare string. Also Attr('n').eq(1) | 5 or any | with a non-ConditionBase.

Common situations: Mixing up OR with attribute_exists/attribute_not_exists shorthand; forgetting .eq() on the second side; treating | like a bitwise OR on values.

Related errors


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