Enforcing Application Invariants with DynamoDB Conditional Writes
Learn how to use ConditionExpression in DynamoDB UpdateItem to prevent race conditions and ensure attributes are updated only when they match an expected value or are absent.
18 Feb 2026, 12:00 UTC

Desired outcome
Update a DynamoDB item only when a specific attribute matches an expected value or does not exist, thereby preventing race conditions that could overwrite concurrent changes.
Prerequisites
- An existing DynamoDB table with a known primary key (partition key and, if used, sort key).
- AWS credentials that grant the
dynamodb:UpdateItemaction on the table. - The AWS SDK for your language installed and configured (e.g.,
aws-sdkfor Node.js v3 orboto3for Python).
Procedure
Call UpdateItem with a ConditionExpression that encodes the invariant. Provide the table name, the item’s key, an UpdateExpression to modify attributes, and any needed expression attribute names/values.
Example (Node.js v3 SDK)
import { DynamoDBClient, UpdateItemCommand } from "@aws-sdk/client-dynamodb";
const client = new DynamoDBClient({}); // credentials from environment or profile
async function conditionalUpdate() {
const params = {
TableName: "MyTable",
Key: {
pk: { S: "USER#123" },
sk: { S: "PROFILE" }
},
UpdateExpression: "SET #cnt = :newVal",
ConditionExpression: "attribute_not_exists(#cnt) OR #cnt = :expected",
ExpressionAttributeNames: {
"#cnt": "counter"
},
ExpressionAttributeValues: {
":newVal": { N: "5" },
":expected": { N: "3" }
},
ReturnValues: "ALL_NEW" // optional, to see updated attributes
};
try {
const data = await client.send(new UpdateItemCommand(params));
console.log("Update succeeded", data.Attributes);
} catch (err) {
if (err.name === "ConditionalCheckFailedException") {
console.warn("Condition not met; item unchanged");
} else {
throw err; // unexpected error
}
}
}
conditionalUpdate();
Example (Python boto3)
import boto3
from botocore.exceptions import ClientError
dynamodb = boto3.client('dynamodb')
def conditional_update():
try:
response = dynamodb.update_item(
TableName='MyTable',
Key={'pk': {'S': 'USER#123'}, 'sk': {'S': 'PROFILE'}},
UpdateExpression='SET #cnt = :newVal',
ConditionExpression='attribute_not_exists(#cnt) OR #cnt = :expected',
ExpressionAttributeNames={'#cnt': 'counter'},
ExpressionAttributeValues={':newVal': {'N': '5'}, ':expected': {'N': '3'}},
ReturnValues='ALL_NEW'
)
print('Update succeeded:', response['Attributes'])
except ClientError as e:
if e.response['Error']['Code'] == 'ConditionalCheckFailedException':
print('Condition not met; item unchanged')
else:
raise
conditional_update()
Expected checks
On success, the service returns an HTTP 200 response and, if ReturnValues is set, the updated attributes. No exception is thrown. On failure, DynamoDB throws ConditionalCheckFailedException (or returns it via the SDK), indicating that the condition evaluated to false.
Recovery options
- Retry the operation with a different condition (e.g., read the latest value first).
- Perform a
PutItemwith the same condition to create the item if it does not exist. - Log the conflict for manual review or apply application‑specific conflict resolution.
- Use exponential backoff when retrying to avoid throttling.
Limitations and cost considerations
Even when the condition fails, DynamoDB consumes write capacity units (or request units in on‑demand mode) because the condition evaluation is performed as a write attempt. This can affect cost and throttling if failed conditional writes are frequent. Condition evaluation is strongly consistent with the latest committed data, but a subsequent read may be eventually consistent unless you request a strongly consistent read.
Practical verification
- After a successful conditional write, call
GetItemwithConsistentRead: trueand verify that the updated attribute matches the intended value. - To confirm a failing condition, repeat the same
UpdateItemand catchConditionalCheckFailedException; then run a strongly consistentGetItemand ensure the item remains unchanged. - Test with a non‑existent key: if the condition expects
attribute_not_exists(#attr)on a key that does not exist, the operation should fail withConditionalCheckFailedException, showing that the condition applies only to existing items.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.