ExpressionAttributeNames contains invalid key: Syntax error
TL;DR — The problem is the placeholder key on the left side of your ExpressionAttributeNames map, not the attribute it points to. A placeholder must be # followed by plain letters, digits, or underscores (#name, #p0). If you put the real attribute name — with its dots, hyphens, + signs, or spaces — into the placeholder itself, DynamoDB rejects the map. Keep placeholders boring; put the messy real name on the right side.
What it means
ValidationException: ExpressionAttributeNames contains invalid key:
Syntax error; key: "#my.attribute"ExpressionAttributeNames maps a placeholder token (used inside your expression) to a real attribute name. DynamoDB validates the placeholder's syntax before touching your data: it must start with # and contain only characters that are valid inside an expression token. Special characters that mean something in the expression grammar — . (path separator), -, +, spaces — make the placeholder itself unparseable, and the whole request is rejected with this ValidationException.
Why it happens
- The real attribute name was copied into the placeholder — e.g.
{"#stats.daily": "stats.daily"}. The dot in the key is a syntax error, regardless of what it maps to. - Special characters in the placeholder — hyphens (
#user-id),+signs, or spaces. Only alphanumerics and underscores are safe after the#. - A missing
#— keys inExpressionAttributeNamesmust begin with#;{"name": "name"}is invalid. - A library auto-generating placeholders from attribute names that contain dots or special characters, passing the character straight through.
How to fix it
Use simple placeholders and map each to the real name:
{ ExpressionAttributeNames: {'#p0': 'user-id', '#p1': 'stats'}, KeyConditionExpression: '#p0 = :uid' }For nested paths, alias each segment separately — one placeholder per path element, joined by a literal dot in the expression:
// read stats.daily where the item has a top-level "stats" map { ProjectionExpression: '#s.#d', ExpressionAttributeNames: {'#s': 'stats', '#d': 'daily'} }Note the flip side: if the attribute's actual name contains a literal dot (one attribute named
"stats.daily", not a nested path), a single placeholder for the whole name is exactly what you want —{'#sd': 'stats.daily'}— so the dot is treated as part of the name, not a path separator.Check what your wrapper generates — if an ODM/helper builds the map for you, log the final request and inspect the placeholder keys it produced.
Working with attributes whose names are full of dots and dashes? The DynoTable desktop app builds its queries with properly aliased names automatically, whatever the attribute is called.
Related errors
- Attribute name is a reserved keyword — the other big reason to use
ExpressionAttributeNames. - Value provided in ExpressionAttributeNames unused in expressions
- Value provided in ExpressionAttributeValues unused
- Learn: Expression names & values
References
- Expression attribute names (aliases) in DynamoDB — Amazon DynamoDB Developer Guide
- Query — Amazon DynamoDB API Reference
- Reserved words in DynamoDB — Amazon DynamoDB Developer Guide
Last verified 2026-07-13 against the official AWS documentation linked above.