boto/boto3 · error · DynamoDBNeedsKeyConditionError

Attribute object {value.name} is of type {type(value)}. KeyC

Error message

Attribute object {value.name} is of type {type(value)}. KeyConditionExpression only supports Attribute objects of type Key

What it means

Raised in _build_expression_component when is_key_condition is True and an AttributeBase operand is an Attr (not a Key). DynamoDB's KeyConditionExpression may only reference partition/sort key attributes represented by Key(...); using Attr(...) (which denotes a non-key item attribute) is semantically invalid for key conditions. The builder explicitly checks isinstance(value, Key) and rejects Attr objects.

Source

Thrown at boto3/dynamodb/conditions.py:407

        attribute_value_placeholders,
        has_grouped_values,
        is_key_condition,
    ):
        # Continue to recurse if the value is a ConditionBase in order
        # to extract out all parts of the expression.
        if isinstance(value, ConditionBase):
            return self._build_expression(
                value,
                attribute_name_placeholders,
                attribute_value_placeholders,
                is_key_condition,
            )
        # If it is not a ConditionBase, we can recurse no further.
        # So we check if it is an attribute and add placeholders for
        # its name
        elif isinstance(value, AttributeBase):
            if is_key_condition and not isinstance(value, Key):
                raise DynamoDBNeedsKeyConditionError(
                    f'Attribute object {value.name} is of type {type(value)}. '
                    f'KeyConditionExpression only supports Attribute objects '
                    f'of type Key'
                )
            return self._build_name_placeholder(
                value, attribute_name_placeholders
            )
        # If it is anything else, we treat it as a value and thus placeholders
        # are needed for the value.
        else:
            return self._build_value_placeholder(
                value, attribute_value_placeholders, has_grouped_values
            )

    def _build_name_placeholder(self, value, attribute_name_placeholders):
        attribute_name = value.name
        # Figure out which parts of the attribute name that needs replacement.
        attribute_name_parts = ATTR_NAME_REGEX.findall(attribute_name)

View on GitHub (pinned to c7b4afac23)

Solutions

  1. Use Key(...) for every attribute in a KeyConditionExpression: Key('pk').eq('v').
  2. For sort-key range conditions also use Key: Key('pk').eq('v') & Key('sk').begins_with('s').
  3. Reserve Attr(...) exclusively for FilterExpression / ConditionExpression (non-key filters).

Example fix

# before
table.query(KeyConditionExpression=Attr('pk').eq('user#1'))

# after
table.query(KeyConditionExpression=Key('pk').eq('user#1'))
Defensive patterns

Strategy: type-guard

Validate before calling

from boto3.dynamodb.conditions import Key, AttributeBase

def validate_key_condition(expr):
    # crude check: ensure no Attr leaks in by scanning repr
    if 'Attr' in repr(expr):
        raise TypeError('KeyConditionExpression must use Key(), not Attr()')
    return expr

Type guard

from boto3.dynamodb.conditions import Key

def is_key(obj) -> bool:
    return isinstance(obj, Key)

Try / catch

from boto3.exceptions import DynamoDBNeedsKeyConditionError
try:
    table.query(KeyConditionExpression=kce)
except DynamoDBNeedsKeyConditionError:
    # rebuild using Key() instead of Attr()
    table.query(KeyConditionExpression=Key('pk').eq('v'))

Prevention

When it happens

Trigger: table.query(KeyConditionExpression=Attr('pk').eq('v')) — used Attr instead of Key. Also Key('pk').eq('v') & Attr('sk').begins_with('s') where the sort-key side is an Attr.

Common situations: Copy-pasting a FilterExpression (built with Attr) into KeyConditionExpression; not knowing that Key vs Attr is enforced positionally; renaming attributes during a schema migration.

Related errors


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