DynamoDB will happily let you name an attribute name, but it will also throw a fit if you try to use it in certain contexts.
Let’s say you’re trying to update an item and want to change the name attribute. You might write something like this:
import boto3
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('YourTableName')
response = table.update_item(
Key={
'id': 'user123'
},
UpdateExpression='SET name = :val',
ExpressionAttributeValues={
':val': 'Alice'
}
)
This looks perfectly reasonable, right? You’re setting the name attribute to 'Alice'. But if you run this, you’ll get an error:
An error occurred (ValidationException) when calling the UpdateItem operation: Invalid UpdateExpression: Incorrect attribute name: name. Reserved keywords cannot be used as attribute names.
DynamoDB has a list of reserved words that can’t be used directly as attribute names in certain expressions. name is one of them. Others include common words like id, key, value, type, status, and many more. This is because these words have special meaning within DynamoDB’s expression syntax.
The Solution: Expression Attribute Names
The way to bypass this is to tell DynamoDB what you mean when you use a reserved word in your expression. You do this with ExpressionAttributeNames. This parameter maps a placeholder name (prefixed with #) to your actual attribute name.
Here’s how you’d fix the previous example:
import boto3
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('YourTableName')
response = table.update_item(
Key={
'id': 'user123'
},
UpdateExpression='SET #n = :val', # Use #n as a placeholder for 'name'
ExpressionAttributeNames={
'#n': 'name' # Map #n to the actual attribute name 'name'
},
ExpressionAttributeValues={
':val': 'Alice'
}
)
Now, UpdateExpression uses #n as a placeholder, and ExpressionAttributeNames tells DynamoDB that #n actually refers to the attribute named name. This allows you to use reserved words without conflict.
Why This Happens (The Mechanics)
DynamoDB parses your UpdateExpression and ExpressionAttributeValues separately. When it sees SET name = :val, it tries to interpret name directly as an attribute name. Since name is a reserved word, it flags it as an error because it doesn’t know if you intend it as the attribute name or as the name keyword in some other context. By using ExpressionAttributeNames, you explicitly declare that #n is the attribute name, thus disambiguating your intent and allowing the operation to proceed.
Other Common Scenarios and Causes
-
Querying with Reserved Words:
- Diagnosis: If you try to query or scan using a
FilterExpressionorKeyConditionExpressionthat uses a reserved word directly. - Example Error:
Invalid FilterExpression: Attribute name is not a valid attribute: name - Fix:
response = table.query( KeyConditionExpression=Key('partitionKey').eq('someValue') & Key('#n').eq('Alice'), ExpressionAttributeNames={ '#n': 'name' } ) - Why it works: Similar to
UpdateItem, you’re telling DynamoDB that#nrepresents the attributenamewithin your query’s conditions.
- Diagnosis: If you try to query or scan using a
-
Using Reserved Words in
UpdateExpressionfor Deletion:- Diagnosis: Attempting to
REMOVEa reserved word attribute. - Example Error:
Invalid UpdateExpression: Incorrect attribute name: name. Reserved keywords cannot be used as attribute names. - Fix:
response = table.update_item( Key={ 'id': 'user123' }, UpdateExpression='REMOVE #n', ExpressionAttributeNames={ '#n': 'name' } ) - Why it works: Again, the placeholder
#ncombined with theExpressionAttributeNamesmapping resolves the ambiguity.
- Diagnosis: Attempting to
-
Using Reserved Words in
UpdateExpressionfor Deletion of a Map or Set Element:- Diagnosis: Trying to remove a specific element from a map or set attribute that is a reserved word.
- Example Error:
Invalid UpdateExpression: Incorrect attribute name: name. Reserved keywords cannot be used as attribute names. - Fix:
response = table.update_item( Key={ 'id': 'user123' }, UpdateExpression='REMOVE #m.#n', # Example for a map attribute 'm' with key 'name' ExpressionAttributeNames={ '#m': 'metadata', # Assuming 'metadata' is a map attribute '#n': 'name' } ) - Why it works: You need to specify the path to the element using placeholders for each level if any part of the path involves a reserved word.
-
Using Reserved Words in
ProjectionExpression:- Diagnosis: If you try to select a reserved word attribute in a
ProjectionExpressionwithout usingExpressionAttributeNames. - Example Error:
Invalid ProjectionExpression: Attribute name is not a valid attribute: name - Fix:
response = table.get_item( Key={ 'id': 'user123' }, ProjectionExpression='#n', ExpressionAttributeNames={ '#n': 'name' } ) - Why it works: You are instructing DynamoDB to retrieve the attribute that is aliased by
#n, which you’ve defined asname.
- Diagnosis: If you try to select a reserved word attribute in a
-
Reserved Words within Lists or Maps as Attribute Values:
- Diagnosis: While
ExpressionAttributeValuesgenerally handles values fine, if you are constructing a complex expression that references a reserved word as part of a condition on a list or map element, you might still needExpressionAttributeNames. For example, checking if a list contains a map where a key is a reserved word. - Fix:
response = table.scan( FilterExpression=Attr('myList').contains({'#n': 'Alice'}) # This is a simplified example, actual syntax can be more complex ) # The above is PSEUDOCODE for illustration. # A real example would be more like: response = table.scan( FilterExpression=ForAllValues(Attr('myList'), lambda x: x.get('#n') == 'Alice'), ExpressionAttributeNames={'#n': 'name'} ) - Why it works: DynamoDB needs to understand that the key within the map being checked against is
name, not a keyword.
- Diagnosis: While
-
Using
SELECTwith Reserved Words:- Diagnosis: When using
ScanorQuerywithSelect='SPECIFIC_ATTRIBUTES'and the attribute names you’re selecting are reserved words. - Example Error:
Invalid ProjectionExpression: Attribute name is not a valid attribute: name - Fix:
response = table.query( KeyConditionExpression=Key('partitionKey').eq('someValue'), Select='SPECIFIC_ATTRIBUTES', ProjectionExpression='#n, #a', # Assuming 'a' is another attribute ExpressionAttributeNames={ '#n': 'name', '#a': 'age' } ) - Why it works: Explicitly mapping reserved attribute names in
ProjectionExpressionwhen specifyingSPECIFIC_ATTRIBUTESensures DynamoDB correctly identifies which data to retrieve.
- Diagnosis: When using
Once you’ve correctly applied ExpressionAttributeNames to all reserved words used in your expressions, the next error you’ll encounter is likely a ProvisionedThroughputExceededException if your table can’t handle the load.