DynamoDB AccessDeniedException: How to Read and Fix It
AccessDeniedException in DynamoDB is an IAM problem — a missing action, the wrong resource ARN, an index not covered, or a deny policy. How to decode the message and fix each cause.
On this page
AccessDeniedException is always an IAM authorization problem — the caller isn’t allowed to do what it asked. The good news: the message tells you almost exactly what to fix. A typical one reads:
AccessDeniedException: User: arn:aws:iam::123456789012:user/dev is not authorized to
perform: dynamodb:Query on resource: arn:aws:dynamodb:us-east-1:123456789012:table/Orders
because no identity-based policy allows the dynamodb:Query action
Three parts matter:
- The principal —
user/dev(or a role). Who is being denied. - The action —
dynamodb:Query. What they tried to do. - The resource —
table/Orders. Which resource it applies to.
Fix the IAM statement so that principal is allowed that action on that resource. Here are the specific causes.
Cause 1: Missing action
Every DynamoDB API maps to its own IAM action, and they’re not bundled. Granting dynamodb:GetItem does not grant dynamodb:Query, dynamodb:PutItem, dynamodb:BatchWriteItem, or dynamodb:UpdateItem. Read the denied action from the message and add it:
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:Query",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
"dynamodb:BatchGetItem",
"dynamodb:BatchWriteItem"
],
"Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/Orders"
}
Cause 2: Index not covered by the resource ARN
A Global or Local Secondary Index has a distinct ARN:
arn:aws:dynamodb:us-east-1:123456789012:table/Orders/index/GSI1
If your policy’s Resource lists only the table, a Query against GSI1 is denied even though the same query on the base table works. Cover both:
"Resource": [
"arn:aws:dynamodb:us-east-1:123456789012:table/Orders",
"arn:aws:dynamodb:us-east-1:123456789012:table/Orders/index/*"
]
See Secondary indexes for how index queries differ from base-table queries.
Cause 3: Wrong resource ARN (region / account / name)
The ARN encodes region and account id. A policy scoped to us-east-1 won’t authorize a call made in us-west-2, and a name typo (Orders vs orders-prod) leaves the real table uncovered. Make the ARN match the table you’re actually calling — including region and account.
Cause 4: An explicit Deny or a permissions boundary
In IAM, an explicit Deny always wins, regardless of any Allow. If everything looks granted but you’re still denied, check for:
- A
Denystatement in the identity policy, an SCP (Service Control Policy) at the org level, or a permissions boundary that caps what the role can do. - A resource-based policy (on the table) that denies the principal.
The message hint — “because no identity-based policy allows…” vs “with an explicit deny in…” — tells you which situation you’re in.
Cause 5: The wrong identity entirely
Sometimes the policy is fine but the call runs as an unexpected principal — a Lambda using its execution role instead of the role you assigned, an EC2 instance profile, or a stale local profile. Confirm who you actually are:
aws sts get-caller-identity
Compare that ARN to the principal in the error. If they differ, fix the credential chain, not the policy.
Encryption: KMS denials in disguise
If the table uses a customer-managed KMS key, the caller also needs kms:Decrypt / kms:GenerateDataKey on that key. A missing KMS permission surfaces as an access-denied on the DynamoDB operation. Grant the KMS actions on the key ARN too.
Diagnostic checklist
- Read the message: note the principal, action, resource.
- Is the action in the policy? (GetItem ≠ Query ≠ BatchWriteItem.)
- Querying an index? Add the
.../index/*ARN. - Does the ARN’s region, account, name match the real table?
- Any explicit Deny / SCP / boundary overriding the allow?
aws sts get-caller-identity— are you the principal you think you are?- Customer-managed KMS key? Add
kms:Decrypt+kms:GenerateDataKey.
Test your access quickly
The fastest way to confirm a permission fix worked is to connect the exact profile/role and try the operation. Tablyne connects with your AWS profiles (including SSO and assumed roles) and shows immediately which tables and indexes that identity can actually reach — so you can verify an IAM change without writing a throwaway script. Download Tablyne free.
Frequently asked questions
How do I read a DynamoDB AccessDeniedException message?
The message names the principal, the action, and the resource: 'User: arn:...:user/dev is not authorized to perform: dynamodb:Query on resource: arn:...:table/Orders'. Those three parts tell you exactly which IAM statement to fix — the identity, the missing action, and the resource ARN it must allow.
Why do I get access denied on a Query even though GetItem works?
Each API maps to its own IAM action. A policy that grants dynamodb:GetItem does not grant dynamodb:Query. Grant every action your code uses — GetItem, Query, PutItem, UpdateItem, BatchWriteItem, etc.
My table policy is correct but querying an index is denied. Why?
A secondary index has its own ARN: arn:...:table/Orders/index/GSI1. A resource that lists only the table ARN doesn't cover the index. Add the index ARN (or table/Orders/index/* ) to the policy's Resource.