KMSAccessDeniedException: Lambda was unable to decrypt the environment variables because KMS access was denied

Short answer

A KMS decrypt was denied. Two very different situations produce it: Lambda itself failing to decrypt your encrypted environment variables before the handler runs, or an SDK call inside your code being denied kms:Decrypt on a key protecting some other resource.

What it means

KMS is unusual among AWS services in requiring permission from two directions. The caller's IAM policy must allow the action, and the key's own policy must permit the caller. Satisfying only one produces a denial, and because most services need only the first, this catches people who have correctly granted every permission they thought was involved.

On Lambda the confusing case is environment variables. If a function uses a customer-managed key to encrypt them, Lambda decrypts them using your execution role before your handler runs. Your code never calls KMS; there is no kms:Decrypt anywhere in your source; and yet the role needs that permission or the function cannot start at all. The failure lands before your first log line, which makes it look like the function is broken rather than unauthorised.

That also explains the most alarming version of this: a function that has run for months, with no deployment, suddenly failing every invocation. Nothing changed in the function. Someone disabled the key, scheduled it for deletion, or tightened its policy, and every execution environment that tries to start now fails at the decrypt step. The four exception names distinguish these — Disabled, InvalidState, NotFound, AccessDenied — and reading which one you got points directly at whether this is a policy problem or a key lifecycle problem.

The second family is more ordinary but reads as more mysterious: an SDK call that fails with a KMS error when you were talking to S3. An object encrypted with a customer-managed key requires kms:Decrypt on that key in addition to s3:GetObject, and the denial surfaces from the KMS side with a message that never mentions the bucket. When a KMS exception appears in a stack trace full of S3 or DynamoDB frames, that is what has happened — and the fix is a second statement in the policy, not a change to the one you have been staring at.

Depends

CloudWatch’s Errors metric: Whether CloudWatch’s Errors metric counts this depends on whether the error escaped your handler, which the log line alone does not settle. When Lambda cannot decrypt environment variables the invocation fails before your code runs and CloudWatch counts it. A KMSAccessDeniedException from an SDK call inside your handler is counted only if it escapes. LogStitch classified the example below from its log level rather than an extracted type.

What it looks like in CloudWatch

This is the shape the failure arrives in: the lines of one invocation scattered among everything else the log group received at the same moment.

CloudWatch Logs Before

7 raw lines, in the order CloudWatch delivered them.

2026-08-29T15:02:18.114Z START RequestId: 6b3f9c02-8e17-4a54-b7d1-3c0e5f2a9b46 Version: $LATEST
2026-08-29T15:02:18.118Z 6b3f9c02-8e17-4a54-b7d1-3c0e5f2a9b46 INFO Export requested key=exports/2026-07/ledger.csv.gz
2026-08-29T15:02:18.402Z 6b3f9c02-8e17-4a54-b7d1-3c0e5f2a9b46 ERROR KMSAccessDeniedException: The ciphertext refers to a customer master key that does not exist, does not exist in this region, or you are not allowed to access.
at throwDefaultError (/var/task/node_modules/@smithy/smithy-client/dist-cjs/index.js:867:20)
at deserializeAws_restXmlGetObjectCommandError (/var/task/node_modules/@aws-sdk/client-s3/dist-cjs/index.js:4412:14)
2026-08-29T15:02:18.688Z END RequestId: 6b3f9c02-8e17-4a54-b7d1-3c0e5f2a9b46
2026-08-29T15:02:18.688Z REPORT RequestId: 6b3f9c02-8e17-4a54-b7d1-3c0e5f2a9b46 Duration: 574.22 ms Billed Duration: 575 ms Memory Size: 512 MB Max Memory Used: 108 MB Status: error

LogStitch After

The same lines, grouped into the invocation they belong to.

Error6b3f9c02-8e17-4a54-b7d1-3c0e5f2a9b46dur 574msbilled 575msmem 108/512MBlogs 5
  1. 15:02:18.114PLAT
    START RequestId: 6b3f9c02-8e17-4a54-b7d1-3c0e5f2a9b46 Version: $LATEST
  2. 15:02:18.118INFO
    INFO	Export requested key=exports/2026-07/ledger.csv.gz
  3. 15:02:18.402ERROR
    ERROR	KMSAccessDeniedException: The ciphertext refers to a customer master key that does not exist, does not exist in this region, or you are not allowed to access.
        at throwDefaultError (/var/task/node_modules/@smithy/smithy-client/dist-cjs/index.js:867:20)
        at deserializeAws_restXmlGetObjectCommandError (/var/task/node_modules/@aws-sdk/client-s3/dist-cjs/index.js:4412:14)
    3 lines
  4. 15:02:18.688PLAT
    END RequestId: 6b3f9c02-8e17-4a54-b7d1-3c0e5f2a9b46
  5. 15:02:18.688PLAT
    REPORT RequestId: 6b3f9c02-8e17-4a54-b7d1-3c0e5f2a9b46	Duration: 574.22 ms	Billed Duration: 575 ms	Memory Size: 512 MB	Max Memory Used: 108 MB	Status: error
Error
Status
574ms
Duration
575ms
Billed
108/512MB
Memory
79%
Headroom
No
Cold start
LogLevelError
Error type

The panel on the right is generated by running the excerpt on the left through the same parser that powers the free web stitcher — it is what the tool actually produces for this input, not an illustration of it.

How to confirm it from the logs

Look at where the error sits relative to START. If it appears before your first log line, and the text mentions environment variables, Lambda could not start the function — the failure is in the platform's own decrypt and no code of yours has run. If it appears mid-invocation with an SDK stack trace, the denial is on a key protecting a bucket, a table, a queue, or a secret.

Distinguish the four related exceptions by name, because the fixes differ. KMSAccessDeniedException is a permissions problem. KMSDisabledException means the key is disabled. KMSInvalidStateException usually means it is pending deletion. KMSNotFoundException means the key does not exist — commonly because it lived in an account or region you are no longer talking to.

Causes, most likely first

1

The execution role cannot use the key that encrypts the environment variables

How to confirm

Check whether the function has a customer-managed KMS key configured for environment variables, and whether that key's policy permits the execution role to kms:Decrypt. Lambda performs this decrypt as your role, so the role needs the grant even though your code never calls KMS.

2

The key was disabled, scheduled for deletion, or its policy was rewritten

How to confirm

Describe the key and read its state and policy. A function that has worked for months and suddenly cannot start, with no deployment in between, is nearly always a key whose state or policy changed underneath it.

3

The role lacks kms:Decrypt for a resource it reads

How to confirm

Read the stack trace. If the denial comes from inside an S3, DynamoDB, SQS or Secrets Manager call, the resource is encrypted with a customer-managed key and the role needs kms:Decrypt on that key in addition to the service permission.

4

The key policy does not grant access, only the IAM policy does

How to confirm

Check the key policy itself, not just the role. KMS requires the key's own policy to permit the principal — an IAM policy alone is not sufficient unless the key policy delegates to IAM for that account.

Fixes

Fix 1

Grant the execution role kms:Decrypt on the key

For encrypted environment variables the role needs kms:Decrypt on the key Lambda uses. Adding a condition scoped to the Lambda service keeps the grant narrow.

yaml- Effect: Allow
  Action:
    - kms:Decrypt
  Resource: !GetAtt EnvKey.Arn
  Condition:
    StringEquals:
      kms:ViaService: !Sub "lambda.${AWS::Region}.amazonaws.com"
Fix 2

Check the key's own policy, not just the role's

KMS evaluates the key policy as well as the caller's IAM policy. A role with kms:Decrypt in its own policy is still denied if the key policy does not permit it or delegate to IAM.

bashaws kms describe-key --key-id alias/lambda-env \
  --query 'KeyMetadata.{State:KeyState,Manager:KeyManager}'

aws kms get-key-policy --key-id alias/lambda-env \
  --policy-name default --output text
Fix 3

Add kms:Decrypt alongside the service permission for encrypted resources

Reading from an encrypted bucket or table needs both the service action and the key permission. Granting only the first produces a denial that names KMS rather than the service, which is why it reads as unrelated to the call you made.

yaml- Effect: Allow
  Action: [s3:GetObject]
  Resource: !Sub "${ExportBucket.Arn}/*"
- Effect: Allow
  Action: [kms:Decrypt, kms:DescribeKey]
  Resource: !GetAtt ExportKey.Arn

Also seen as

The same underlying failure, worded differently by a different runtime, SDK version, or logging layer. All of these land here — there is no separate page for each phrasing.

Lambda was unable to decrypt the environment variables because KMS access was denied. Please check the function's KMS key settings.KMSDisabledExceptionKMSInvalidStateExceptionKMSNotFoundExceptionThe ciphertext refers to a customer master key that does not exist

Errors that show up alongside this one, or that people mistake for it.

References

LogStitch finds this automatically, across every invocation in your account.

Paste a log excerpt into the free web stitcher and see it grouped, classified, and measured in your browser — nothing is uploaded. Or run the Mac app against your own AWS profiles and get the same view over every function you own.