boto/boto3 · error · DynamoDBOperationNotSupportedError

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

Error message

NOT 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 AttributeBase.__invert__ (~) when the unary NOT operator is applied to a bare Attr/Key that has not been turned into a ConditionBase. You cannot negate a raw attribute; you must first build a condition (e.g. Attr('x').eq(1)) and then negate that condition with ~.

Source

Thrown at boto3/dynamodb/conditions.py:74

                return True
        return False

    def __ne__(self, other):
        return not self.__eq__(other)


class AttributeBase:
    def __init__(self, name):
        self.name = name

    def __and__(self, value):
        raise DynamoDBOperationNotSupportedError('AND', self)

    def __or__(self, value):
        raise DynamoDBOperationNotSupportedError('OR', self)

    def __invert__(self):
        raise DynamoDBOperationNotSupportedError('NOT', self)

    def eq(self, value):
        """Creates a condition where the attribute is equal to the value.

        :param value: The value that the attribute is equal to.
        """
        return Equals(self, value)

    def lt(self, value):
        """Creates a condition where the attribute is less than the value.

        :param value: The value that the attribute is less than.
        """
        return LessThan(self, value)

    def lte(self, value):
        """Creates a condition where the attribute is less than or equal to the
           value.

View on GitHub (pinned to c7b4afac23)

Solutions

  1. Call a comparison method before ~: ~(Attr('x').eq(1)).
  2. For 'attribute does not exist' use Attr('x').not_exists() directly (no ~ needed).
  3. Remember that ~ negates a ConditionBase, not an AttributeBase.

Example fix

# before
fe = ~Attr('x')

# after (negate a condition)
fe = ~Attr('x').eq(1)
# or, if intent was 'attribute does not exist'
fe = Attr('x').not_exists()
Defensive patterns

Strategy: type-guard

Validate before calling

from boto3.dynamodb.conditions import ConditionBase, AttributeBase

def safe_not(item):
    if isinstance(item, AttributeBase) and not isinstance(item, ConditionBase):
        raise TypeError(f'Cannot apply ~ to bare Attr/Key {item.name!r}; call a comparison method first')
    return ~item

Type guard

from boto3.dynamodb.conditions import ConditionBase

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

Try / catch

from boto3.exceptions import DynamoDBOperationNotSupportedError
try:
    fe = ~attr_x
except DynamoDBOperationNotSupportedError:
    fe = ~attr_x.eq(1)

Prevention

When it happens

Trigger: table.scan(FilterExpression=~Attr('x')) — no comparison method called. Also ~Key('pk') or ~Attr('x').

Common situations: Trying to express 'NOT x' (attribute does not exist) but using ~Attr instead of Attr('x').not_exists(); translating logical NOT naively.

Related errors


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