An operand in the update expression has an incorrect data type

TL;DR — The update's action is type-checked against what's actually stored (and against the value you sent), and one of them doesn't fit: ADD against an attribute that isn't a number or set, list_append against something that isn't a list, or a set operand whose element type doesn't match the stored set. Match the operand to the stored type — or fix the item whose type drifted.

What it means

ValidationException: An operand in the update expression has an incorrect data type

Update actions have strict type rules: ADD works only on numbers and sets (SS/NS/BS), DELETE only on sets, and list_append takes two lists. DynamoDB evaluates the expression against the item's current attribute types at write time — so the same request can succeed on one item and fail on another whose attribute holds a different type. A close cousin is rejected earlier, at expression parse time, when the sent value's type can never fit the operator:

ValidationException: Invalid UpdateExpression: Incorrect operand type for
operator or function; operator: ADD, operand type: LIST

Why it happens

  • ADD counter :one but counter is stored as a string — e.g. the item was imported with "5" (type S) instead of 5 (type N).
  • ADD tags :t with a list (L) operandADD collects into sets, not lists. Lists are appended with SET + list_append.
  • list_append(mylist, :v) where mylist is a map, set, or scalar — both arguments must be lists (wrap a single element as a one-item list).
  • Set element type mismatchADD/DELETE with a string set (SS) operand against a stored number set (NS), or vice versa.
  • Type drift across items — mixed writers (an old import, another service) left the same attribute as N on some items and S on others, so the update fails only sometimes.

How to fix it

  1. Counters: store the value as a number and send a number operand:

    UpdateExpression: 'ADD #c :one',
    ExpressionAttributeValues: {':one': 1}   // {N: "1"} — not a string
  2. Lists: use SET with list_append, and wrap the new element in a list:

    UpdateExpression: 'SET #l = list_append(if_not_exists(#l, :empty), :new)',
    ExpressionAttributeValues: {':new': ['item'], ':empty': []}
  3. Sets: keep ADD/DELETE operands the same set type as the stored attribute (SS with SS, NS with NS).

  4. Fix drifted items — find the items whose attribute carries the wrong type and rewrite them once; the "same update fails on some items" symptom is always data, not code.

Type drift is invisible in most consoles — the DynoTable desktop app displays each attribute's actual stored type next to its value, so a "5"-as-string counter stands out immediately.

References

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

Work with DynamoDB without the Console

DynoTable is a fast desktop client for DynamoDB — browse tables, run SQL-style queries, and edit items locally.