DynamoDB Conditional Write in Python (boto3)
A conditional write attaches a ConditionExpression to a put_item, update_item, or delete_item: DynamoDB evaluates it against the current item and applies the write only if it holds — atomically, with no read-modify-write race. It's DynamoDB's optimistic-locking primitive. This example guards an update with a version check.
Code
import boto3
client = boto3.client("dynamodb")
# Update the item only if nobody changed it since we read version 7.
try:
client.update_item(
TableName="Music",
Key={"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "Cubano Chant"}},
UpdateExpression="SET #upd0 = :updValue0, #version = :newVersion",
ConditionExpression="attribute_exists(#cond0) AND #version = :expectedVersion",
ExpressionAttributeNames={"#upd0": "Genre", "#version": "Version", "#cond0": "Artist"},
ExpressionAttributeValues={
":updValue0": {"S": "Latin Jazz"},
":expectedVersion": {"N": "7"},
":newVersion": {"N": "8"},
},
ReturnValuesOnConditionCheckFailure="ALL_OLD",
)
print("Updated to version 8")
except client.exceptions.ConditionalCheckFailedException as e:
# With ReturnValuesOnConditionCheckFailure="ALL_OLD", the current item
# rides back on the exception — no extra read to see what beat you.
print("Lost the race — item is now:", e.response.get("Item"))Explanation
ConditionExpression— checked against the stored item atomically with the write. Available functions:attribute_exists,attribute_not_exists,attribute_type,contains,begins_with,size, plus comparators (=,<>,<,>,<=,>=,BETWEEN,IN) andAND/OR/NOT.- The two workhorse idioms:
attribute_exists(#key)on an update = "update-only, never create". Without it,update_itemon a missing key silently creates the item (the accidental-upsert bug).attribute_not_exists(#key)on a put = "create-only, never overwrite" — shown on the PutItem page.
- Optimistic locking — read the item (version 7), then write with
#version = :expectedVersionwhile bumping to 8. Two concurrent writers can't both win; the loser getsConditionalCheckFailedException, re-reads, and retries on the new version. ReturnValuesOnConditionCheckFailure="ALL_OLD"— puts the current item on the exception response (e.response), saving the follow-upget_itemafter a failed check. It consumes no read capacity; a failed conditional write still bills its write attempt.- On the resource API the same guard is
table.update_item(..., ConditionExpression=Attr("Version").eq(7) & Attr("Artist").exists())with native Python values.
Do it visually
Condition expressions are exactly what the DynamoDB Expression Builder builds — pick the function, get the expression + name/value maps as runnable boto3 code.
To edit items with generated, reviewable expressions instead of hand-typed placeholders — download DynoTable.
Related examples
- DynamoDB conditional write in Node.js — the same optimistic lock with AWS SDK v3.
- DynamoDB conditional write with the AWS CLI — the same optimistic lock from the shell.
- DynamoDB PutItem in Python — the create-only
attribute_not_existsput. - DynamoDB condition expressions — every function, with patterns.
- Enforcing uniqueness on multiple attributes — conditions + transactions combined.
- DynamoDB ConditionalCheckFailedException — when the failed check is expected, and how to handle it cheaply.
References
- UpdateItem — Amazon DynamoDB API Reference
- DynamoDB.Client.update_item — Boto3 documentation
- Condition expressions — Amazon DynamoDB Developer Guide
- DynamoDB read and write operations (capacity unit consumption) — Amazon DynamoDB Developer Guide
Last verified 2026-07-13 against the official AWS documentation linked above.