DynamoDB returns TransactionCanceledException when a TransactWriteItems or TransactGetItems operation fails to complete. This error occurs when one or more items in the transaction cannot be processed due to conflicts, throttling, validation errors, or other constraints.
The TransactionCanceledException error indicates that DynamoDB could not complete a multi-item transaction operation. Unlike single-item operations that succeed or fail independently, DynamoDB transactions provide atomicity, consistency, isolation, and durability (ACID) guarantees across multiple items. When any condition prevents the transaction from committing, the entire transaction is rolled back and this error is returned. Transactions in DynamoDB can be cancelled for several reasons: 1. **Conditional check failures**: A condition expression in one of the transaction items evaluates to false 2. **Transaction conflicts**: Another transaction or operation is modifying the same items concurrently 3. **Throttling**: The table or index exceeds its provisioned throughput capacity 4. **Validation errors**: Invalid item attributes, incorrect data types, or size limits exceeded 5. **Internal errors**: Temporary DynamoDB service issues or partition unavailability The "Transaction cancelled" message is the generic error text, but the actual cause is detailed in the CancellationReasons array returned with the error response.
The error response includes a CancellationReasons array that details why each transaction item failed. Analyze this array to understand the root cause:
try {
await dynamodb.transactWriteItems(params).promise();
} catch (error) {
if (error.name === 'TransactionCanceledException') {
console.log('Transaction cancelled. Reasons:', error.CancellationReasons);
// Example CancellationReasons structure:
// [
// { Code: 'ConditionCheckFailed', Message: 'The conditional request failed' },
// { Code: 'None', Message: null }, // This item would have succeeded
// { Code: 'ThrottlingError', Message: 'Rate exceeded' }
// ]
// Identify which items failed and why
error.CancellationReasons.forEach((reason, index) => {
if (reason.Code !== 'None') {
console.log(`Item ${index} failed: ${reason.Code} - ${reason.Message}`);
}
});
}
}Common cancellation codes:
- ConditionCheckFailed: Condition expression evaluated to false
- TransactionConflict: Concurrent modification conflict
- ThrottlingError: Throughput limits exceeded
- ValidationError: Invalid data or size limits
- ItemCollectionSizeLimitExceeded: Collection would exceed 10GB
When ConditionCheckFailed appears in CancellationReasons, review and adjust your condition expressions:
// Problematic condition that might fail
const params = {
TransactItems: [
{
Update: {
TableName: 'Orders',
Key: { orderId: '123' },
UpdateExpression: 'SET #status = :newStatus',
ConditionExpression: '#status = :expectedStatus',
ExpressionAttributeNames: { '#status': 'status' },
ExpressionAttributeValues: {
':newStatus': 'SHIPPED',
':expectedStatus': 'PROCESSING' // Might not match current value
}
}
}
]
};
// Solution: Check current state before transaction or use more flexible conditions
// Option 1: Fetch current state first
const currentItem = await dynamodb.getItem({
TableName: 'Orders',
Key: { orderId: '123' }
}).promise();
// Option 2: Use attribute_exists or attribute_not_exists for existence checks
const saferParams = {
TransactItems: [
{
Update: {
TableName: 'Orders',
Key: { orderId: '123' },
UpdateExpression: 'SET #status = :newStatus',
ConditionExpression: 'attribute_exists(orderId)', // Less restrictive
ExpressionAttributeNames: { '#status': 'status' },
ExpressionAttributeValues: { ':newStatus': 'SHIPPED' }
}
}
]
};Best practices:
- Use attribute_exists() or attribute_not_exists() for existence checks
- Avoid complex conditions that depend on frequently changing attributes
- Consider fetching current state before attempting conditional updates
- Implement fallback logic for condition failures
For TransactionConflict errors, implement proper retry logic with exponential backoff:
async function executeTransactionWithRetry(params, maxRetries = 3) {
let retryCount = 0;
while (retryCount < maxRetries) {
try {
return await dynamodb.transactWriteItems(params).promise();
} catch (error) {
if (error.name === 'TransactionCanceledException') {
const hasConflict = error.CancellationReasons?.some(
reason => reason.Code === 'TransactionConflict'
);
if (hasConflict && retryCount < maxRetries) {
retryCount++;
const delay = Math.min(1000, 50 * Math.pow(2, retryCount)); // Exponential backoff
const jitter = delay * 0.1 * Math.random(); // Add jitter
await new Promise(resolve => setTimeout(resolve, delay + jitter));
continue;
}
}
throw error; // Re-throw if not a conflict or retries exhausted
}
}
throw new Error('Max retries exceeded for transaction');
}
// Use ClientRequestToken for idempotent retries
const transactionParams = {
TransactItems: [
{ Put: { TableName: 'Table1', Item: item1 } },
{ Update: { TableName: 'Table2', Key: key2, UpdateExpression: 'SET #count = #count + :inc' } }
],
ClientRequestToken: 'unique-id-' + Date.now() // Ensures idempotent retries
};
await executeTransactionWithRetry(transactionParams);Conflict resolution strategies:
- Use exponential backoff with jitter to avoid synchronized retries
- Implement ClientRequestToken for idempotent retry safety
- Consider optimistic concurrency control with version numbers
- Reduce transaction size to minimize contention windows
When ThrottlingError appears, adjust capacity or implement backoff:
# Check current provisioned capacity
aws dynamodb describe-table --table-name YourTableName
# Increase provisioned capacity if consistently throttled
aws dynamodb update-table --table-name YourTableName --provisioned-throughput 'ReadCapacityUnits=200,WriteCapacityUnits=100'
# For on-demand tables hitting account limits, request limit increase
aws service-quotas request-service-quota-increase --service-code dynamodb --quota-code L-12345678 --desired-value 40000Throttling mitigation:
- Monitor CloudWatch metrics: ThrottledRequests, ConsumedReadCapacityUnits, ConsumedWriteCapacityUnits
- Enable auto-scaling for provisioned tables
- Consider on-demand capacity for unpredictable workloads
- Implement circuit breakers to fail fast during sustained throttling
- Reduce transaction size to lower capacity consumption per operation
For ValidationError cancellations, check item attributes and sizes:
// Common validation issues:
// 1. Item size exceeds 400KB limit
// 2. Attribute value exceeds 400KB limit
// 3. Invalid data type for attribute
// 4. Reserved keyword used without expression attributes
// Validate item size before transaction
function getItemSizeBytes(item) {
return Buffer.byteLength(JSON.stringify(item), 'utf8');
}
const item1Size = getItemSizeBytes(item1);
const item2Size = getItemSizeBytes(item2);
if (item1Size > 400 * 1024 || item2Size > 400 * 1024) {
console.error('Item exceeds 400KB limit');
// Implement compression or split into multiple items
}
// Check for reserved keywords
const params = {
TransactItems: [
{
Put: {
TableName: 'Users',
Item: {
userId: '123',
name: 'John',
// 'status' is a reserved keyword - need expression attributes
'#status': 'active' // WRONG - will cause validation error
}
}
}
]
};
// Correct approach with expression attributes
const correctParams = {
TransactItems: [
{
Put: {
TableName: 'Users',
Item: {
userId: '123',
name: 'John',
status: 'active' // OK - not using reserved keyword as attribute name
}
}
}
]
};Validation best practices:
- Implement pre-transaction validation for item sizes
- Avoid DynamoDB reserved keywords as attribute names
- Use consistent data types across items
- Test with small transactions before scaling up
Implement monitoring and optimize transaction patterns:
# Monitor transaction metrics in CloudWatch
aws cloudwatch get-metric-statistics --namespace "AWS/DynamoDB" --metric-name "TransactionConflict" --dimensions Name=TableName,Value=YourTableName --statistics Sum --start-time "$(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ)" --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --period 300
# Check UserErrors for validation issues
aws cloudwatch get-metric-statistics --namespace "AWS/DynamoDB" --metric-name "UserErrors" --dimensions Name=TableName,Value=YourTableName --statistics Sum --start-time "$(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ)" --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --period 300Transaction optimization strategies:
- Keep transactions small (≤ 25 items, ≤ 4MB total)
- Group related items that change together
- Avoid mixing high-contention and low-contention items
- Consider eventual consistency for non-critical updates
- Use DynamoDB Streams for cross-table coordination instead of transactions when possible
- Implement dead-letter queues for failed transactions with manual recovery
## DynamoDB Transaction Internals and Best Practices
### Transaction Isolation and Consistency
DynamoDB transactions provide serializable isolation, the strongest isolation level:
- Read isolation: Transactions see a consistent snapshot of all items at transaction start
- Write isolation: All writes in a transaction become visible atomically
- Conflict detection: Pessimistic locking at the partition key level
- Rollback: Automatic rollback if any item fails
### Transaction Limits and Constraints
1. Item count: Maximum 25 unique items per transaction
2. Total size: Maximum 4MB for all transaction items combined
3. Item size: Each item ≤ 400KB
4. Duration: Transactions typically complete within seconds
5. Throughput: Consumes 2x write capacity units (WCUs) for writes, 2x read capacity units (RCUs) for reads
### CancellationReason Codes Reference
| Code | Meaning | Typical Resolution |
|------|---------|-------------------|
| ConditionCheckFailed | Condition expression evaluated to false | Adjust condition or check current state |
| TransactionConflict | Concurrent modification conflict | Implement retry with backoff |
| ThrottlingError | Throughput limits exceeded | Increase capacity or reduce rate |
| ValidationError | Invalid data or size limits | Fix data validation |
| ItemCollectionSizeLimitExceeded | Collection would exceed 10GB | Archive old data or split collections |
| InternalServerError | Temporary service issue | Retry with exponential backoff |
| AccessDeniedException | IAM permissions insufficient | Update IAM policies |
### Performance Considerations
1. Hot partitions: Transactions on hot keys increase conflict rates
2. Retry storms: Aggressive retries can amplify throttling and conflicts
3. Capacity planning: Transactions consume 2x normal capacity units
4. Latency: Transaction operations have higher latency than single-item operations
### Alternative Patterns
When transactions cause persistent issues, consider:
1. Optimistic concurrency control: Use version numbers and conditional writes
2. Saga pattern: Break transactions into compensatable steps
3. DynamoDB Streams + Lambda: Process related changes asynchronously
4. Single-table design: Reduce need for cross-table transactions
### Debugging Tools
1. AWS X-Ray: Trace transaction flows and identify bottlenecks
2. CloudWatch Logs Insights: Query transaction error patterns
3. DynamoDB Console: Monitor metrics and throttling events
4. AWS Support: Contact for persistent transaction issues
### When to Avoid Transactions
- High-throughput write scenarios (> 1000 writes/sec)
- Frequently contested hot keys
- Cross-region operations (use global tables instead)
- Non-atomic consistency requirements
- Very large items (> 100KB each)
ValidationException: The provided key element does not match the schema
How to fix "ValidationException: The provided key element does not match the schema" in DynamoDB
UnrecognizedClientException: The security token included in the request is invalid
How to fix "UnrecognizedClientException: The security token included in the request is invalid" in DynamoDB
RequestLimitExceeded: Throughput exceeds the current throughput limit for your account
How to fix "RequestLimitExceeded: Throughput exceeds the current throughput limit for your account" in DynamoDB
TableNotFoundException: Table not found
TableNotFoundException: Table not found in DynamoDB
InternalServerError: Internal Server Error
How to fix "InternalServerError" in DynamoDB