boto/boto3 · error · DynamoDBNeedsConditionError

Expecting a ConditionBase object. Got

Error message

Expecting a ConditionBase object. Got {value} of type {type(value)}. Use AttributeBase object methods (i.e. Attr().eq()). to generate ConditionBase instances.

What it means

When building a ConditionExpression or KeyConditionExpression, boto3 requires the top-level condition argument to be a ConditionBase. If you pass a raw attribute, scalar, list, or string to ConditionExpressionBuilder.build_condition, it raises DynamoDBNeedsConditionError(condition) because there is no defined serialization for non-condition input.

Solutions

  1. Always pass a ConditionBase (result of Attr()/Key() comparison methods) to filter_expression / key_condition_expression.
  2. If you want a string-based expression, do not use boto3 conditions; use the botocore-level ExpressionAttributeNames/Values with a raw string instead.
  3. Validate dynamic conditions with isinstance(cond, ConditionBase) before passing them to the API.

Example fix

# before
table.query(KeyConditionExpression=Key('pk'), ...)  # bare Key, no comparison
# after
table.query(KeyConditionExpression=Key('pk').eq('user#42'), ...)
Defensive patterns

Strategy: type-guard

Validate before calling

from boto3.dynamodb.conditions import ConditionBase
def assert_condition(c, label):
    assert isinstance(c, ConditionBase), \
        f'{label} must be a ConditionBase; got {type(c).__name__}'
    return c

Type guard

from boto3.dynamodb.conditions import ConditionBase
def is_condition_expression(v) -> bool:
    return isinstance(v, ConditionBase)

Try / catch

from boto3.exceptions import DynamoDBNeedsConditionError
try:
    table.scan(FilterExpression=expr)
except DynamoDBNeedsConditionError:
    expr = Attr('x').eq(1)  # rebuild properly

Prevention

When it happens

Trigger: Passing Attr('x') directly as filter_expression, passing a value like 'x = :v' as a hand-written string, or passing a dict/None where a ConditionBase is expected. Common when constructing Table.query(...) or Table.scan(...) conditions programmatically.

Common situations: Mixing boto3.dynamodb.conditions with raw string expressions; passing a Python value instead of Attr().eq(value); forgetting to call .eq()/.begins_with() on an attribute.

Related errors


AI-assisted analysis of boto/boto3@6e10b029c1 (2026-08-11). Data as JSON: /api/errors/99c4ca6cb38bbd48. Report an issue: GitHub.

Appendix: source

Thrown at boto3/dynamodb/conditions.py:344

        :type condition: ConditionBase
        :param condition: A condition to be built into a condition expression
            string with any necessary placeholders.

        :type is_key_condition: Boolean
        :param is_key_condition: True if the expression is for a
            KeyConditionExpression. False otherwise.

        :rtype: (string, dict, dict)
        :returns: Will return a string representing the condition with
            placeholders inserted where necessary, a dictionary of
            placeholders for attribute names, and a dictionary of
            placeholders for attribute values. Here is a sample return value:

            ('#n0 = :v0', {'#n0': 'myattribute'}, {':v1': 'myvalue'})
        """
        if not isinstance(condition, ConditionBase):
            raise DynamoDBNeedsConditionError(condition)
        attribute_name_placeholders = {}
        attribute_value_placeholders = {}
        condition_expression = self._build_expression(
            condition,
            attribute_name_placeholders,
            attribute_value_placeholders,
            is_key_condition=is_key_condition,
        )
        return BuiltConditionExpression(
            condition_expression=condition_expression,
            attribute_name_placeholders=attribute_name_placeholders,
            attribute_value_placeholders=attribute_value_placeholders,
        )

    def _build_expression(
        self,
        condition,
        attribute_name_placeholders,

View on GitHub (pinned to 6e10b029c1)