{"record":{"id":"be1e55cea141d9fe","repo":"boto/boto3","slug":"attribute-object-value-name-is-of-type-type-val","errorCode":null,"errorMessage":"Attribute object {value.name} is of type {type(value)}. KeyConditionExpression only supports Attribute objects of type Key","messagePattern":"Attribute object (.+?) is of type (.+?)\\. KeyConditionExpression only supports Attribute objects of type Key","errorType":"exception","errorClass":"DynamoDBNeedsKeyConditionError","httpStatus":null,"severity":"error","filePath":"boto3/dynamodb/conditions.py","lineNumber":407,"sourceCode":"        attribute_value_placeholders,\n        has_grouped_values,\n        is_key_condition,\n    ):\n        # Continue to recurse if the value is a ConditionBase in order\n        # to extract out all parts of the expression.\n        if isinstance(value, ConditionBase):\n            return self._build_expression(\n                value,\n                attribute_name_placeholders,\n                attribute_value_placeholders,\n                is_key_condition,\n            )\n        # If it is not a ConditionBase, we can recurse no further.\n        # So we check if it is an attribute and add placeholders for\n        # its name\n        elif isinstance(value, AttributeBase):\n            if is_key_condition and not isinstance(value, Key):\n                raise DynamoDBNeedsKeyConditionError(\n                    f'Attribute object {value.name} is of type {type(value)}. '\n                    f'KeyConditionExpression only supports Attribute objects '\n                    f'of type Key'\n                )\n            return self._build_name_placeholder(\n                value, attribute_name_placeholders\n            )\n        # If it is anything else, we treat it as a value and thus placeholders\n        # are needed for the value.\n        else:\n            return self._build_value_placeholder(\n                value, attribute_value_placeholders, has_grouped_values\n            )\n\n    def _build_name_placeholder(self, value, attribute_name_placeholders):\n        attribute_name = value.name\n        # Figure out which parts of the attribute name that needs replacement.\n        attribute_name_parts = ATTR_NAME_REGEX.findall(attribute_name)","sourceCodeStart":389,"sourceCodeEnd":425,"githubUrl":"https://github.com/boto/boto3/blob/6e10b029c1326a437932a8b24f38af69fd986e15/boto3/dynamodb/conditions.py#L389-L425","documentation":"KeyConditionExpression may only reference Key attributes (the partition and sort keys). During expression building, if is_key_condition is True and the value is an AttributeBase that is not a Key (i.e. an Attr), boto3 raises DynamoDBNeedsKeyConditionError. This enforces the DynamoDB rule that only key attributes may appear in KeyConditionExpression; non-key attributes must go in FilterExpression.","triggerScenarios":"Passing Attr('non_key_col').eq(...) inside KeyConditionExpression; building a KeyCondition from an Attr object by mistake; combining a Key condition with an Attr condition via & and putting the result in key_condition_expression.","commonSituations":"Confusing Attr (for filter_expression) with Key (for key_condition_expression); trying to filter on a non-keyed attribute inside the key condition to 'save' a filter step.","solutions":["Use Key(name) for everything in KeyConditionExpression; move non-key predicates to FilterExpression with Attr(name).","Check that every attribute appearing in your key condition was created with Key(), not Attr().","When combining conditions for the key expression, ensure both operands descend from Key comparisons."],"exampleFix":"# before\ntable.query(\n    KeyConditionExpression=Key('pk').eq('u1') & Attr('created').gt(0),\n)\n# after\ntable.query(\n    KeyConditionExpression=Key('pk').eq('u1') & Key('sk').gt('2024'),\n    FilterExpression=Attr('created').gt(0),\n)","handlingStrategy":"type-guard","validationCode":"from boto3.dynamodb.conditions import Key, ConditionBase\n# Walk the AST of the condition and ensure all leaves are Key, not Attr\ndef only_keys(c):\n    if isinstance(c, ConditionBase):\n        return all(only_keys(v) for v in c._values)\n    return isinstance(c, Key)","typeGuard":"from boto3.dynamodb.conditions import Key, AttributeBase\ndef is_key_attr(v) -> bool:\n    return isinstance(v, Key) or not isinstance(v, AttributeBase)","tryCatchPattern":"from boto3.exceptions import DynamoDBNeedsKeyConditionError\ntry:\n    table.query(KeyConditionExpression=kc)\nexcept DynamoDBNeedsKeyConditionError:\n    # move the Attr-based predicate into FilterExpression\n    table.query(KeyConditionExpression=kc_key_only, FilterExpression=attr_filter)","preventionTips":["Reserve Key() for KeyConditionExpression and Attr() for FilterExpression.","Do not combine Key conditions with Attr conditions in the key expression.","Document which attributes are keys when building queries dynamically."],"tags":["dynamodb","conditions","key","query","boto3"],"backgroundTag":null,"analyzedSha":"6e10b029c1326a437932a8b24f38af69fd986e15","analyzedAt":"2026-08-11T20:52:47.213Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}