DynamoDB TransactionCanceledException — ConditionalCheckFailed

TL;DR — Your TransactWriteItems was canceled and the CancellationReasons array holds a ConditionalCheckFailed entry. One of the transaction's condition expressions didn't hold, so DynamoDB rolled back every action atomically. Read the reasons array — its entries are positional, one per requested item — to find which item's condition failed, then fix that item's precondition or the data.

What it means

TransactionCanceledException: Transaction cancelled, please refer cancellation
reasons for specific reasons [ConditionalCheckFailed, None, None]
# CancellationReasons[0] = { Code: "ConditionalCheckFailed",
#                            Message: "The conditional request failed" }

TransactWriteItems is all-or-nothing. If any single action's ConditionExpression evaluates false, DynamoDB cancels the whole request and reports a per-item reason list. ConditionalCheckFailed at position i means the i-th item's condition wasn't met — the transaction never partially applied.

Why it happens

  • Optimistic-lock miss — a version = :v / attribute_not_exists(pk) guard failed because another writer already changed or created the item.
  • Uniqueness guard tripped — an attribute_not_exists(pk) insert lost a race, so the item already exists.
  • Stale read — the condition was built from a value that has since changed.
  • Reason list misread — the array is positional, ordered like your TransactItems; a None at a slot means that item was fine, ConditionalCheckFailed marks the one that failed. (Other codes in the array — TransactionConflict, ItemCollectionSizeLimitExceeded, ProvisionedThroughputExceeded, ThrottlingError, ValidationError — mean something different.)

How to fix it

  1. Inspect CancellationReasons in the exception and find the index with ConditionalCheckFailed — that's the failing action.
  2. Re-read the item and decide: retry with a fresh precondition (optimistic-lock retry loop) or surface a conflict to the caller.
  3. Fix the condition if it's wrong — e.g. attribute_not_exists(pk) on an item that legitimately already exists.
  4. Add ReturnValuesOnConditionCheckFailure: ALL_OLD to the failing action so DynamoDB returns the item that broke the condition (great for debugging).
  5. Bound your retries — a persistently failing condition is a real business conflict, not a transient error; don't retry forever.

Debugging a failed transaction by hand? Inspect the current item state in the DynoTable desktop app to see exactly why the precondition didn't hold before you retry.

FAQ

How do I know which item in my transaction failed? Read the CancellationReasons array on the TransactionCanceledException. It's positional — one entry per requested item, in order. The entry with code ConditionalCheckFailed identifies the action whose condition expression evaluated false; entries with code None succeeded.

Is a ConditionalCheckFailed inside a transaction retryable? Not automatically. It's a real precondition conflict, not a transient error. Re-read the item, decide whether the write still applies, and retry with a fresh condition — or surface the conflict to the user.

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.