DynamoDB returns ResourceNotFoundException when you attempt to access a table, index, or other resource that doesn't exist or isn't in an active state. This HTTP 400 error occurs when the requested resource name is incorrect, the resource is still being created, or it has been deleted.
The ResourceNotFoundException error in DynamoDB indicates that your application is trying to access a resource (table, index, etc.) that cannot be found in your AWS account and region. This error occurs in several scenarios: 1. **Table doesn't exist**: You're referencing a table name that hasn't been created yet or was deleted 2. **Table still creating**: The table exists but is in CREATING state and not yet ready for operations 3. **Index not active**: A global secondary index (GSI) or local secondary index (LSI) is still being created or is in a non-ACTIVE state 4. **Wrong region**: The table exists in a different AWS region than your application is configured to use 5. **Wrong account**: The table exists in a different AWS account DynamoDB returns this error with HTTP status code 400 to indicate a client-side issue that needs to be corrected before retrying. Unlike throttling errors, this error is not retryable without first fixing the underlying resource issue.
First, check if the table exists and confirm the exact name:
# List all tables in the region
aws dynamodb list-tables
# Describe a specific table to check its status
aws dynamodb describe-table --table-name YourTableName
# Check table status (should be ACTIVE)
aws dynamodb describe-table --table-name YourTableName \
--query 'Table.TableStatus'
# For cross-account access, verify the full ARN
aws dynamodb describe-table --table-arn 'arn:aws:dynamodb:us-east-1:123456789012:table/YourTableName'Common mistakes to check:
- Case sensitivity: DynamoDB table names are case-sensitive
- Special characters: Table names must match exactly
- Region mismatch: Table exists in us-west-2 but SDK configured for us-east-1
- Account mismatch: Using credentials for wrong AWS account
If your operation involves an index, verify its status:
# Check all indexes on a table
aws dynamodb describe-table --table-name YourTableName \
--query 'Table.GlobalSecondaryIndexes[*].{IndexName:IndexName,Status:IndexStatus}'
# Check specific index status
aws dynamodb describe-table --table-name YourTableName \
--query "Table.GlobalSecondaryIndexes[?IndexName=='YourIndexName'].IndexStatus"
# Wait for index to become ACTIVE
while true; do
status=$(aws dynamodb describe-table --table-name YourTableName \
--query "Table.GlobalSecondaryIndexes[?IndexName=='YourIndexName'].IndexStatus" \
--output text)
if [ "$status" = "ACTIVE" ]; then
echo "Index is ACTIVE"
break
fi
echo "Index status: $status - waiting..."
sleep 10
doneIndex creation timing:
- GSI creation can take several minutes for large tables
- Index status shows: CREATING → ACTIVE/DELETING
- Queries fail with ResourceNotFoundException until index is ACTIVE
- Consider creating indexes before deploying application
Verify your application's AWS configuration matches the table's location:
// AWS SDK v3 for JavaScript - check region configuration
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
const client = new DynamoDBClient({
region: 'us-east-1', // Must match table region
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
sessionToken: process.env.AWS_SESSION_TOKEN // For temporary credentials
}
});
// For cross-account access, verify assumed role permissions
const stsClient = new STSClient({ region: 'us-east-1' });
const identity = await stsClient.send(new GetCallerIdentityCommand({}));
console.log('Current AWS account:', identity.Account);
// Environment variable check
console.log('AWS Region:', process.env.AWS_REGION);
console.log('AWS Default Region:', process.env.AWS_DEFAULT_REGION);Configuration checks:
- Verify AWS_REGION environment variable is set correctly
- Check AWS credentials have permissions for the table
- For assumed roles, verify the role has DynamoDB access
- For local development, check ~/.aws/config and ~/.aws/credentials files
Add defensive programming to handle resource creation race conditions:
async function ensureTableExists(tableName, createTableParams) {
try {
await dynamodb.describeTable({ TableName: tableName }).promise();
console.log('Table ' + tableName + ' exists');
return true;
} catch (error) {
if (error.name === 'ResourceNotFoundException') {
console.log('Table ' + tableName + ' not found, creating...');
try {
await dynamodb.createTable(createTableParams).promise();
console.log('Table ' + tableName + ' created successfully');
// Wait for table to become active
await dynamodb.waitFor('tableExists', { TableName: tableName }).promise();
return true;
} catch (createError) {
console.error('Failed to create table ' + tableName + ':', createError);
return false;
}
}
throw error;
}
}
// For index-dependent operations
async function waitForIndexActive(tableName, indexName, maxWaitSeconds = 300) {
const startTime = Date.now();
while (Date.now() - startTime < maxWaitSeconds * 1000) {
try {
const data = await dynamodb.describeTable({ TableName: tableName }).promise();
const index = data.Table.GlobalSecondaryIndexes?.find(idx => idx.IndexName === indexName);
if (index?.IndexStatus === 'ACTIVE') {
console.log('Index ' + indexName + ' is ACTIVE');
return true;
}
console.log('Index ' + indexName + ' status: ' + (index?.IndexStatus || 'NOT_FOUND'));
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds
} catch (error) {
console.error('Error checking index status:', error);
return false;
}
}
throw new Error('Index ' + indexName + ' did not become active within ' + maxWaitSeconds + ' seconds');
}Best practices:
- Use AWS SDK waiters (tableExists, tableNotExists) when available
- Implement idempotent table/index creation in deployment scripts
- Add health checks that verify table accessibility before accepting traffic
- Log detailed error context including region, account, and table name
Investigate the error in AWS monitoring tools:
# Search CloudTrail for recent DescribeTable failures
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=DescribeTable \
--start-time $(date -u -d '1 hour ago' +%s) \
--end-time $(date -u +%s) \
--query 'Events[?contains(CloudTrailEvent, "ResourceNotFoundException")]'
# Check CloudWatch metrics for table operations
aws cloudwatch get-metric-statistics \
--namespace AWS/DynamoDB \
--metric-name UserErrors \
--dimensions Name=TableName,Value=YourTableName \
--start-time $(date -u -d '1 hour ago' +%s) \
--end-time $(date -u +%s) \
--period 300 \
--statistics Sum
# Check if table was recently deleted
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=DeleteTable \
--start-time $(date -u -d '24 hours ago' +%s) \
--end-time $(date -u +%s) \
--query 'Events[?contains(CloudTrailEvent, "YourTableName")]'Log analysis tips:
- Look for DeleteTable events preceding the errors
- Check UserErrors metric spikes in CloudWatch
- Verify IAM role changes that might affect permissions
- Review deployment logs for table creation/modification times
- Check if auto-scaling or other automation deleted the table
If using CloudFormation, CDK, or Terraform, verify resource definitions:
# CloudFormation example - check table name references
Resources:
MyDynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: MyApplicationTable # This name must match application code
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
# Reference in other resources must use correct name
MyLambdaFunction:
Type: AWS::Lambda::Function
Properties:
Environment:
Variables:
TABLE_NAME: !Ref MyDynamoDBTable # Passes logical ID, not table name
# Should be: TABLE_NAME: MyApplicationTable// AWS CDK TypeScript - common issues
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
const table = new dynamodb.Table(this, 'MyTable', {
tableName: 'my-application-table', // Explicit name
partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
});
// Common mistake: using logical ID instead of table name
const wrongTableName = table.tableName; // Returns 'my-application-table'
const logicalId = table.node.id; // Returns 'MyTable' - NOT the table name
// Pass correct name to Lambda environment
lambdaFunction.addEnvironment('TABLE_NAME', table.tableName);Infrastructure issues:
- CloudFormation stack rollbacks leaving tables in inconsistent state
- CDK/CloudFormation logical IDs vs. physical resource names
- Terraform state drift causing resource recreation
- Deployment order issues (app deployed before table creation completes)
- Environment-specific table name prefixes/suffixes not accounted for
## ResourceNotFoundException Deep Dive
### Table Creation States and Timing
DynamoDB tables go through specific states during creation:
1. CREATING (1-2 minutes typically): Table being provisioned, all operations fail with ResourceNotFoundException
2. ACTIVE: Table ready for operations
3. UPDATING: Table configuration changing (adding GSIs, updating capacity)
4. DELETING: Table being deleted, may still accept some operations briefly
Important: Even after CreateTable API returns successfully, the table may remain in CREATING state for 1-2 minutes. Applications should wait for ACTIVE status before using the table.
### Global Secondary Index (GSI) Creation
GSI creation has additional considerations:
- Backfilling: DynamoDB copies existing table data to the new index (can take hours for large tables)
- Online indexing: New writes go to both table and index during creation
- Query behavior: Queries using the index fail with ResourceNotFoundException until backfilling completes and status becomes ACTIVE
- Creation time: Depends on table size - can be minutes to hours
### Cross-Account and Cross-Region Access
ResourceNotFoundException can indicate permission or configuration issues:
Cross-account access requires:
1. IAM role in table account with DynamoDB permissions
2. IAM role in application account with sts:AssumeRole permission
3. Application assuming the role before accessing DynamoDB
4. Resource-based policies (not commonly used with DynamoDB)
Cross-region issues:
- SDK default region vs. table actual region mismatch
- Environment variables (AWS_REGION) overriding SDK configuration
- Multi-region application logic pointing to wrong region
### Table Naming Constraints
DynamoDB table names have specific rules that can cause ResourceNotFoundException:
- Length: 3-255 characters
- Characters: a-z, A-Z, 0-9, . (dot), - (hyphen), _ (underscore)
- Case sensitivity: "MyTable" ≠ "mytable" ≠ "MYTABLE"
- Reserved names: Avoid "aws", "dynamodb", etc. as prefixes
### Common Anti-Patterns
1. Hardcoded table names: Different environments (dev/staging/prod) need different table names
2. Missing waiters: Not waiting for table/index creation to complete
3. Race conditions: Multiple processes trying to create/use table simultaneously
4. Assumed region: Relying on SDK defaults instead of explicit configuration
5. Credential confusion: Using personal IAM user credentials instead of application role
### Debugging Checklist
When facing ResourceNotFoundException:
1. [ ] Run aws dynamodb list-tables in the correct region
2. [ ] Verify table exists and is ACTIVE with describe-table
3. [ ] Check index status if query uses GSI/LSI
4. [ ] Confirm AWS credentials have DynamoDB permissions
5. [ ] Verify AWS region configuration matches table region
6. [ ] Check CloudTrail for recent DeleteTable events
7. [ ] Review deployment logs for table creation timing
8. [ ] Test with AWS CLI using same credentials/region
### When to Contact AWS Support
- Suspected DynamoDB service issue affecting table visibility
- Table appears in list-tables but describe-table fails
- Unexplained table disappearance without DeleteTable events
- Cross-account access issues after verifying IAM configurations
ImportConflictException: There was a conflict when attempting to import to the table
How to fix 'ImportConflictException: There was a conflict when attempting to import to the table' in DynamoDB
TrimmedDataAccessException: The requested data has been trimmed
How to fix "TrimmedDataAccessException: The requested data has been trimmed" in DynamoDB Streams
GlobalTableNotFoundException: Global Table not found
How to fix "GlobalTableNotFoundException: Global Table not found" in DynamoDB
InvalidExportTimeException: The specified ExportTime is outside of the point in time recovery window
How to fix "InvalidExportTimeException: The specified ExportTime is outside of the point in time recovery window" in DynamoDB
MissingAuthenticationTokenException: Missing Authentication Token
How to fix "MissingAuthenticationTokenException: Missing Authentication Token" in DynamoDB