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

  1. Querying with Reserved Words:

    • Diagnosis: If you try to query or scan using a FilterExpression or KeyConditionExpression that 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 #n represents the attribute name within your query’s conditions.
  2. Using Reserved Words in UpdateExpression for Deletion:

    • Diagnosis: Attempting to REMOVE a 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 #n combined with the ExpressionAttributeNames mapping resolves the ambiguity.
  3. Using Reserved Words in UpdateExpression for 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.
  4. Using Reserved Words in ProjectionExpression:

    • Diagnosis: If you try to select a reserved word attribute in a ProjectionExpression without using ExpressionAttributeNames.
    • 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 as name.
  5. Reserved Words within Lists or Maps as Attribute Values:

    • Diagnosis: While ExpressionAttributeValues generally 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 need ExpressionAttributeNames. 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.
  6. Using SELECT with Reserved Words:

    • Diagnosis: When using Scan or Query with Select='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 ProjectionExpression when specifying SPECIFIC_ATTRIBUTES ensures DynamoDB correctly identifies which data to retrieve.

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.

Want structured learning?

Take the full Dynamodb course →