The 'ResourceInUseException: Resource already exists' error occurs when you attempt to create a DynamoDB table, index, or import with a name that already exists. This typically happens in CI/CD deployments, infrastructure-as-code tools, or when redeploy scripts don't check for existing resources.
The "ResourceInUseException: Resource already exists" error indicates that DynamoDB detected an attempt to create a resource (table, global secondary index, or import) with a name that is already in use. This error commonly occurs in these scenarios: - Creating a table in CloudFormation or Serverless Framework when the table already exists from a previous deployment - Running Terraform or AWS CDK without checking if resources were previously created - Import operations that attempt to import data into an already-existing table - Global secondary index (GSI) creation when the index already exists - Infrastructure deployments with removal policies set to RETAIN, which prevents deletion but allows recreation attempts - Retry logic in scripts that don't account for the state after the first successful creation This error occurs because AWS DynamoDB resource names must be globally unique within your AWS account and region for a given resource type. Unlike some other AWS services that accept duplicate creation requests idempotently, DynamoDB strictly enforces name uniqueness and rejects creation of existing resources.
First, verify whether the DynamoDB table already exists in your AWS account and region:
# List all DynamoDB tables in your region
aws dynamodb list-tables --region us-east-1
# Describe a specific table to check status and details
aws dynamodb describe-table --table-name my-table-name --region us-east-1
# Check table status in AWS Console
# Go to DynamoDB > Tables and search for the table nameIf the table exists, you have two options:
1. Use the existing table (recommended)
2. Delete the table and recreate it (only if safe to delete data)
If the table already exists and you want to use it, modify your infrastructure code to reference the existing resource instead of creating a new one:
Terraform:
# Option 1: Import existing table into Terraform state
terraform import aws_dynamodb_table.my_table my-table-name
# Option 2: Use aws_dynamodb_table data source to reference existing table
data "aws_dynamodb_table" "existing" {
name = "my-table-name"
}
# Reference the existing table in your resources
output "table_arn" {
value = data.aws_dynamodb_table.existing.arn
}AWS CDK (TypeScript/Python):
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
// Option 1: Reference existing table
const existingTable = dynamodb.Table.fromTableName(
this,
'ExistingTable',
'my-table-name'
);
// Option 2: Check and conditionally create
const table = tableExists('my-table-name')
? dynamodb.Table.fromTableName(this, 'Table', 'my-table-name')
: new dynamodb.Table(this, 'Table', {
tableName: 'my-table-name',
partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
});CloudFormation:
Resources:
# Instead of creating, reference existing table
MyTableRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: dynamodb.amazonaws.com
Action: 'sts:AssumeRole'
# Use Outputs to reference the existing table
Outputs:
TableName:
Value: my-table-name
Description: Existing DynamoDB table nameIf you're using CloudFormation with DeletionPolicy: Retain (which prevents table deletion but causes ResourceInUseException on redeploy):
Option 1: Update the CloudFormation stack to reference existing table
Resources:
DynamoDBRole:
Type: AWS::IAM::Role
Properties:
# ... role configuration ...
Outputs:
TableName:
Description: Existing DynamoDB table
Value: my-table-name
TableArn:
Description: ARN of the existing table
Value: !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/my-table-name'Option 2: Delete and recreate the stack (if safe)
# Delete only the stack, keeping the retained resources
aws cloudformation delete-stack --stack-name my-stack-name --region us-east-1
# Wait for deletion to complete
aws cloudformation wait stack-delete-complete --stack-name my-stack-name --region us-east-1
# Verify the table still exists
aws dynamodb describe-table --table-name my-table-name
# Now redeploy the stack (it will find and reuse the existing table)
# Update your template to use the existing table, then:
aws cloudformation create-stack --stack-name my-stack-name --template-body file://template.yamlOption 3: Update DeletionPolicy to DELETE
Resources:
MyDynamoDBTable:
Type: AWS::DynamoDB::Table
DeletionPolicy: DELETE # Change from RETAIN to DELETE
Properties:
TableName: my-table-name
# ... rest of properties ...If using Terraform and the resource exists but your state file doesn't know about it:
# Import the existing table into Terraform state
terraform import aws_dynamodb_table.my_table my-table-name
# Verify the import succeeded
terraform state show aws_dynamodb_table.my_table
# Update your Terraform configuration to match the existing table
# (Copy attributes from the import into your .tf files)
terraform plan # Should show no changes if properly synced
# Apply (no resources should be created)
terraform applyFor multiple environments:
# If using workspaces
terraform workspace select production
terraform import aws_dynamodb_table.my_table my-table-name-prod
terraform workspace select staging
terraform import aws_dynamodb_table.my_table my-table-name-stagingIf the error is about a GSI that already exists:
# Check existing indexes on the table
aws dynamodb describe-table --table-name my-table-name --region us-east-1
# Look for GlobalSecondaryIndexes in the output
# If the index exists, either:
# 1. Don't recreate it
# 2. Delete it first (if safe) and then recreateAWS SDK code example:
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB();
async function createIndexIfNotExists() {
const tableName = 'my-table-name';
const indexName = 'my-gsi';
// Get current table description
const tableDesc = await dynamodb.describeTable({
TableName: tableName
}).promise();
// Check if index already exists
const indexExists = tableDesc.Table.GlobalSecondaryIndexes?.some(
gsi => gsi.IndexName === indexName
);
if (indexExists) {
console.log('Index already exists, skipping creation');
return;
}
// Index doesn't exist, create it
await dynamodb.updateTable({
TableName: tableName,
AttributeDefinitions: [
{ AttributeName: 'newAttr', AttributeType: 'S' }
],
GlobalSecondaryIndexUpdates: [
{
Create: {
IndexName: indexName,
KeySchema: [{ AttributeName: 'newAttr', KeyType: 'HASH' }],
Projection: { ProjectionType: 'ALL' },
ProvisionedThroughput: {
ReadCapacityUnits: 5,
WriteCapacityUnits: 5
}
}
}
]
}).promise();
console.log('Index created successfully');
}
createIndexIfNotExists().catch(err => {
console.error('Error:', err.message);
});If the error occurs during import operations:
# Check import status instead of retrying import
aws dynamodb describe-import --import-arn arn:aws:dynamodb:region:account:table/table-name/import/import-id
# Check all imports for a table
aws dynamodb list-imports --table-arn arn:aws:dynamodb:region:account:table/table-name
# If import succeeded, the table is ready to use
# Don't run the import againBetter approach: Check before importing
#!/bin/bash
TABLE_NAME="my-table-name"
S3_BUCKET="my-bucket"
S3_KEY="exports/table-data.json"
# Check if import is already in progress
IMPORT_STATUS=$(aws dynamodb list-imports \
--table-arn "arn:aws:dynamodb:$(aws configure get region):$(aws sts get-caller-identity --query Account --output text):table/$TABLE_NAME" \
--query 'ImportSummaryList[0].ImportStatus' \
--output text)
if [ "$IMPORT_STATUS" = "IN_PROGRESS" ]; then
echo "Import already in progress, waiting..."
# Wait for completion
aws dynamodb wait table-exists --table-name "$TABLE_NAME"
elif [ "$IMPORT_STATUS" = "COMPLETED" ]; then
echo "Import already completed"
else
echo "Starting new import..."
aws dynamodb import-table \
--s3-bucket-source "S3Bucket=$S3_BUCKET,S3KeyPrefix=$S3_KEY" \
--table-creator "TableName=$TABLE_NAME,AttributeDefinitions=[...],KeySchema=[...]"
fiUpdate your infrastructure code to handle ResourceInUseException gracefully:
Node.js/JavaScript SDK:
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB();
async function createTableIdempotent() {
const tableName = 'my-table-name';
try {
// Check if table exists first
const tableDesc = await dynamodb.describeTable({
TableName: tableName
}).promise();
console.log('Table already exists:', tableDesc.Table.TableStatus);
return tableDesc.Table;
} catch (error) {
if (error.code === 'ResourceNotFoundException') {
// Table doesn't exist, create it
console.log('Creating table...');
const result = await dynamodb.createTable({
TableName: tableName,
KeySchema: [{ AttributeName: 'id', KeyType: 'HASH' }],
AttributeDefinitions: [{ AttributeName: 'id', AttributeType: 'S' }],
BillingMode: 'PAY_PER_REQUEST'
}).promise();
// Wait for table to be ready
await dynamodb.waitFor('tableExists', { TableName: tableName }).promise();
return result.TableDescription;
} else {
throw error; // Re-throw other errors
}
}
}
createTableIdempotent().catch(err => console.error(err));Python Boto3:
import boto3
from botocore.exceptions import ClientError
dynamodb = boto3.client('dynamodb')
def create_table_idempotent(table_name):
try:
# Check if table exists
response = dynamodb.describe_table(TableName=table_name)
print(f"Table already exists: {response['Table']['TableStatus']}")
return response['Table']
except ClientError as e:
if e.response['Error']['Code'] == 'ResourceNotFoundException':
# Create the table
print("Creating table...")
response = dynamodb.create_table(
TableName=table_name,
KeySchema=[{'AttributeName': 'id', 'KeyType': 'HASH'}],
AttributeDefinitions=[{'AttributeName': 'id', 'AttributeType': 'S'}],
BillingMode='PAY_PER_REQUEST'
)
# Wait for table to be ready
waiter = dynamodb.get_waiter('table_exists')
waiter.wait(TableName=table_name)
return response['TableDescription']
else:
raise
if __name__ == '__main__':
create_table_idempotent('my-table-name')Prevent naming conflicts by using environment-specific or timestamped table names:
# Use environment prefix
TABLE_NAME="myapp-$ENVIRONMENT-table" # myapp-prod-table, myapp-staging-table
# Use timestamp for ephemeral tables
TABLE_NAME="myapp-table-$(date +%s)"
# Use hash of deployment ID
DEPLOY_HASH=$(echo -n "$CI_PIPELINE_ID" | md5sum | cut -c1-8)
TABLE_NAME="myapp-table-$DEPLOY_HASH"Terraform example:
variable "environment" {
type = string
}
resource "aws_dynamodb_table" "main" {
name = "myapp-${var.environment}-users"
billing_mode = "PAY_PER_REQUEST"
hash_key = "id"
attribute {
name = "id"
type = "S"
}
}AWS CDK example:
const tableName = process.env.ENVIRONMENT === 'prod'
? 'myapp-prod-table'
: 'myapp-staging-table';
const table = new dynamodb.Table(this, 'UsersTable', {
tableName: tableName,
partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
});DynamoDB Table Name Constraints
Table names in DynamoDB must be:
- Unique within your AWS account and region
- Between 3 and 255 characters long
- Contain only alphanumeric characters and hyphens (A-Z, a-z, 0-9, -)
- Start with a letter or number
CloudFormation DeletionPolicy Behavior
When you set DeletionPolicy to RETAIN:
- The resource is NOT deleted when the CloudFormation stack is deleted
- However, the CloudFormation stack records the resource as "in use"
- Attempting to recreate the table in a new CloudFormation stack will fail because the table physically exists
- You must either import the existing resource into the new stack or manually manage the table outside CloudFormation
Idempotency and AWS Resources
Unlike some AWS services that accept duplicate creation requests without error, DynamoDB enforces strict uniqueness:
- Creating a duplicate EC2 security group returns a different error (InvalidGroup.Duplicate)
- Creating a duplicate Lambda function is rejected but can be updated
- Creating a duplicate DynamoDB table is rejected with ResourceInUseException
Always check for resource existence before creating in DynamoDB.
Infrastructure-as-Code State Management
Different tools handle this differently:
- Terraform: Maintains a state file that tracks created resources; use terraform import to sync
- CloudFormation: Maintains stack state in AWS; use import operations for existing resources
- AWS CDK: Generates CloudFormation; uses the same state management as CloudFormation
- Serverless Framework: Custom resource tracking; can cause conflicts if used with other tools
Never manage the same resources with multiple IaC tools simultaneously.
Global Secondary Indexes (GSIs)
Unlike tables, you cannot create a duplicate GSIโthe table has one GSI with a given name:
- Check existing indexes before attempting to create
- Updating a GSI requires the table to be ACTIVE
- Index creation/deletion can take several minutes
- Monitor index status before making additional changes
Multi-Region DynamoDB (Global Tables)
If using Global Tables:
- Each replica table exists independently in different regions
- ResourceInUseException applies per region
- Same table name can exist in multiple regions without conflict
- Import operations work per-region
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
TransactionCanceledException: Transaction cancelled
How to fix "TransactionCanceledException: Transaction cancelled" 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
InternalServerError: Internal Server Error
How to fix "InternalServerError" in DynamoDB