ValidationException: Invalid UpdateExpression

TL;DR — Your UpdateExpression is malformed. Nine times out of ten it's a reserved keyword (like status, name, size) used directly — swap it for a #placeholder in ExpressionAttributeNames. The message names the exact token.

What it means

Typical messages:

ValidationException: Invalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword: status
ValidationException: Invalid UpdateExpression: Syntax error; token: "=", near: "SET status ="
ValidationException: Invalid UpdateExpression: An expression attribute value used in expression is not defined; attribute value: :s

DynamoDB parses the expression string and rejects anything that isn't valid grammar or references an undefined placeholder.

Why it happens

  • Reserved keyword used raw. DynamoDB has hundreds of reserved wordsstatus, name, size, count, data, year. Used directly in an expression they cause a syntax error. The reserved words checker tests your attribute names against the full list and emits the alias map.
  • Missing ExpressionAttributeNames entry for a #name you referenced.
  • Missing ExpressionAttributeValues entry for a :value you referenced.
  • Wrong verb grammar — mixing clauses incorrectly (SET, REMOVE, ADD, DELETE each have their own syntax), or a stray =.
  • Attribute name with special characters (dots, dashes) used without a placeholder.

How to fix it

  1. Alias every attribute name through ExpressionAttributeNames (#status) — it sidesteps the reserved-word list entirely, so aliasing everything is a safe habit.
  2. Define every :value you reference in ExpressionAttributeValues.
  3. Use the right clause. SET to write/overwrite, REMOVE to delete an attribute, ADD for atomic number/set increments, DELETE to remove from a set.
  4. Run the reserved-word check before you ship. Paste your attribute names into the reserved words checker — it flags every name on the AWS list and prints the #alias map you need.
  5. Build the expression once, copy everywhere. A hand-edited string drifts; generate the full UpdateExpression plus both attribute maps from one source so placeholders stay paired.

Example

import {DynamoDBClient} from '@aws-sdk/client-dynamodb';
import {DynamoDBDocumentClient, UpdateCommand} from '@aws-sdk/lib-dynamodb';

const doc = DynamoDBDocumentClient.from(new DynamoDBClient({}));

await doc.send(
  new UpdateCommand({
    TableName: 'Orders',
    Key: {pk: 'ORDER#1'},
    // #status aliases the reserved word "status"
    UpdateExpression: 'SET #status = :s, updatedAt = :t',
    ExpressionAttributeNames: {'#status': 'status'},
    ExpressionAttributeValues: {':s': 'SHIPPED', ':t': Date.now()}
  })
);

Check first in DynoTable

When an update fails in your app, reproduce it in DynoTable before you change production code. Open the table with ⌘K, select the item, and use the inline update editor — DynoTable aliases reserved attribute names automatically and shows the generated UpdateExpression with both attribute maps. Staging (⌘S) lets you preview the edit and catch syntax errors before commit.

For batch fixes, paste the failing expression into the Expression Builder and compare its output to what your SDK sends. Profile switching (⌘P) keeps test runs on the same account as the error; use Test Connection on Settings → Profiles to confirm the profile matches. See Connect to AWS and Install for profile setup. Cross-check attribute names in the reserved words checker when the error names a specific token like status or data. Aliasing every attribute name — not just reserved ones — is a safe habit that prevents this class of error entirely.

Sources

References

Last verified 2026-07-13 against the official AWS documentation linked above.

Work with DynamoDB without the Console

A fast DynamoDB desktop client that runs the real SQL DynamoDB can’t — JOINs, GROUP BY, aggregates — with visual editing and an AI agent on your own Bedrock keys.

Free 30-day trial, no credit card — then the Free plan with no time limit.