AWS KMS InvalidCiphertextException Fixed: Encryption Context, Lambda Variables, "Ciphertext Refers to a Key That Does Not Exist" and Every KMS Error Code
InvalidCiphertextException from AWS KMS means the ciphertext you passed to Decrypt is not something that KMS key can decrypt as given. The usual causes: a different encryption context from the one used to encrypt (every key and value must match exactly; Lambda environment variables encrypted in the console need {"LambdaFunctionName": "your-function"}), ciphertext damaged by encoding (base64 decoded twice or not at all, a trailing newline, a database column that mangled bytes), passing the encrypted data instead of the encrypted data key in envelope encryption, an AWS Encryption SDK message sent to KMS directly, or an asymmetric key decrypted without its KeyId and EncryptionAlgorithm. A related message, "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", is an access error: the key was deleted, lives in another Region, or your role (or another account) has no permission on it, which is also what S3, Glue and Athena show as 403 on SSE-KMS objects. Below: each cause with a test and fix, Python and CLI examples that work, the full list of KMS error codes, and how to recover data from a key pending deletion.
Jake's Lambda function decrypted an API key that had been encrypted in the Lambda console with "Enable helpers for encryption in transit". It worked in a test script and failed in the function with InvalidCiphertextException every time. Same key, same ciphertext. The console had added an encryption context, the function name, and the test script had copied it by accident while the function code had not.
New to KMS? The cards below cover what KMS is, its costs, S3 403 errors and reading error codes in Python.
KMS in plain words, and why its errors are so vague
If KMS is new to you, four ideas explain every error on this page. A KMS key is a key that never leaves AWS: you send data (up to 4 KB) to KMS, and it sends back ciphertext, scrambled bytes that only that key can turn back into the original. For anything bigger, KMS gives you a data key to encrypt the data yourself, and keeps a copy of that data key encrypted under the KMS key. And an encryption context is a label, such as a customer ID, that is locked to the ciphertext and must be shown again to decrypt it.
KMS errors are vague on purpose. If Decrypt told you "the tenant value is wrong" or "this ciphertext belongs to key X in account Y", an attacker probing your data would learn exactly what to try next. So KMS answers with a short, generic code, and you work out the reason from your own side: the bytes you sent, the context you sent, the key and Region you used, and the permissions you hold. The debugging routine below is built around that.
Every cause of InvalidCiphertextException
| Cause | How to tell | Fix |
|---|---|---|
| Encryption context differs | The Encrypt call (or the service that encrypted) used a context; your Decrypt does not, or a value differs | Pass the identical context map |
| Lambda console encryption helpers | Environment variable encrypted in the console | Context {"LambdaFunctionName": name} |
| Encoding damage | Ciphertext length changed; you base64-decode twice or not at all; a newline crept in | Store and pass the exact bytes; decode base64 exactly once |
| Wrong blob in envelope encryption | You send your encrypted file to KMS instead of the encrypted data key | Decrypt the data key with KMS, then the data locally |
| AWS Encryption SDK message | Data was encrypted with the Encryption SDK (or the S3 Encryption Client) | Decrypt with the same SDK, not with KMS Decrypt |
| Asymmetric key without parameters | An RSA or SM2 key was used to encrypt | Pass KeyId and the same EncryptionAlgorithm |
| Ciphertext from a different key family | Encrypted with a key in another Region that is not a multi-Region replica | Decrypt in the key's own Region |
Jake: "So 'invalid ciphertext' doesn't always mean the data is broken?"
Ethan: "Rarely. Usually the ciphertext is fine and the question is asked slightly differently from the encryption."
A debugging routine that finds the cause in minutes
Because KMS deliberately says little about why a decrypt failed, work through the evidence in this order:
- Check the bytes first. Print the length of the ciphertext where it was created and where you decrypt. A different length means storage or encoding damaged it, and nothing else on this page matters until that is fixed.
- Find the failed call in CloudTrail. Failed
Decryptcalls are recorded with the error code and, in the request parameters, the encryption context you sent. That shows at once whether your code sent a context, and which one. - Find the matching
EncryptorGenerateDataKeyevent for the same key around the time the data was written. Its encryption context is the one you must send. Compare the two side by side; a missing pair or a case difference stands out. - Check the key and Region. Run
describe-keyon the key in the Region your code uses: state Enabled, the expected key spec (symmetric or RSA), and the expected Region or multi-Region status. - Check who encrypted it. If a library (the Encryption SDK, the S3 Encryption Client, a database's client-side encryption) wrote the data, decrypt with that library, not KMS directly.
- Reproduce with the CLI. Save the exact bytes to a file and run
aws kms decrypt --ciphertext-blob fileb://blob.bin --encryption-context .... If the CLI works and your code does not, the difference is in your code's handling of bytes or context.
Most investigations end at step 2 or 3. The CloudTrail record is the one place that shows exactly what was asked of KMS, which is why it is worth checking before changing any code.
Jake: "CloudTrail shows our Decrypt sent an empty encryption context."
Ethan: "And the Encrypt event has LambdaFunctionName in it. There's the whole bug, in two log lines."
Encryption context: the most common cause
An encryption context is a set of non-secret key-value pairs bound to the ciphertext at encryption time, such as {"tenant": "shop-uk"}. KMS requires the exact same pairs to decrypt: same keys, same values, same case. One missing or extra pair, and Decrypt fails with InvalidCiphertextException (it does not tell you which pair, by design).
import boto3
kms = boto3.client("kms", region_name="eu-west-2")
ctx = {"tenant": "shop-uk", "purpose": "payment-token"}
blob = kms.encrypt(KeyId="alias/shop-data", Plaintext=b"tok_8842", EncryptionContext=ctx)["CiphertextBlob"]
kms.decrypt(CiphertextBlob=blob, EncryptionContext=ctx) # works
kms.decrypt(CiphertextBlob=blob) # InvalidCiphertextException
kms.decrypt(CiphertextBlob=blob, EncryptionContext={"tenant": "shop-UK", "purpose": "payment-token"}) # fails: case differs
How to find the context that was used when you did not write the encrypting code: look up the Encrypt or GenerateDataKey event in CloudTrail, where the encryption context appears in the request parameters. Services that encrypt for you use their own contexts (for example S3 uses the bucket or object ARN, Secrets Manager the secret ARN), which is why you decrypt their data through the service, not by calling KMS yourself. Our KMS explainer covers why encryption context exists.
Contexts are also useful, which is why it pays to use them on purpose rather than avoid them. Because the context appears in CloudTrail in plain text, a context such as {"customer": "C-778"} tells you exactly which customer's data each Decrypt call opened, which auditors love. And key policies can require a context, so one key can safely serve many tenants, with each tenant's role allowed to decrypt only ciphertext labeled with its own ID, and nothing belonging to any other tenant. The rule that makes this work is the same rule behind the error: the context must match exactly. Keep it simple, short and derived from data you always have at hand (a record ID, a tenant ID), never from something that changes, like a timestamp or a function's name that might be renamed.
Lambda environment variables encrypted in the console
When you turn on Enable helpers for encryption in transit in the Lambda console and encrypt a variable, the console adds an encryption context of LambdaFunctionName set to the function's name. Your code must send the same:
import os, base64
import boto3
kms = boto3.client("kms")
ENCRYPTED = os.environ["PAYMENT_API_KEY"]
API_KEY = kms.decrypt(
CiphertextBlob=base64.b64decode(ENCRYPTED),
EncryptionContext={"LambdaFunctionName": os.environ["AWS_LAMBDA_FUNCTION_NAME"]},
)["Plaintext"].decode("utf-8")
Two more traps in the same setup: the environment variable holds base64 text, so decode it once before calling KMS; and if you rename or copy the function, the context changes and old ciphertext stops decrypting until you encrypt the value again. For new work, Secrets Manager or Parameter Store SecureString are simpler than encrypted environment variables; see our Secrets Manager explainer.
Jake: "The test script worked because I'd copied the console's snippet with the context in it."
Ethan: "And the function didn't have it. One dictionary, and the 'invalid' ciphertext is valid again."
Base64, bytes and encoding damage
KMS ciphertext is raw binary. Most storage (JSON, environment variables, text columns) needs text, so people base64-encode it, and that is where it breaks:
- Decode exactly once: in the SDKs,
CiphertextBlobtakes the raw bytes; passbase64.b64decode(text), not the text itself, and not something decoded twice. - The AWS CLI v2 expects base64 for blob arguments, or a binary file with
fileb://. Mixing the two (a base64 text file passed withfileb://) sends the wrong bytes. - Trailing newlines and spaces from
echoor copy-paste corrupt the value; strip before decoding. - Database columns with a text collation or a length limit can alter or truncate binary; store base64 in a text column, or use a binary column type.
A CLI round trip that works on Linux and macOS:
aws kms encrypt --key-id alias/shop-data --plaintext fileb://secret.txt \ --query CiphertextBlob --output text | base64 --decode > secret.enc aws kms decrypt --ciphertext-blob fileb://secret.enc \ --query Plaintext --output text | base64 --decode
On Windows, the base64 --decode step above does not exist in PowerShell, and it is the usual reason a decrypt that works on a Mac fails on a Windows PC. PowerShell converts with .NET methods instead:
$b64 = aws kms encrypt --key-id alias/shop-data --plaintext fileb://secret.txt --query CiphertextBlob --output text
[IO.File]::WriteAllBytes("$PWD\secret.enc", [Convert]::FromBase64String($b64.Trim()))
$out = aws kms decrypt --ciphertext-blob fileb://secret.enc --query Plaintext --output text
[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($out.Trim()))
Two Windows-specific traps hide in there. Writing the ciphertext with Out-File or > saves text with an encoding (and sometimes a byte-order mark), not raw bytes, so the file passed to fileb:// is no longer the ciphertext; WriteAllBytes avoids that. And .Trim() removes the line ending the CLI prints, which would otherwise break the base64 decoding. In Command Prompt, certutil -decode in.b64 out.bin does the base64 step if PowerShell is not an option.
A quick check that the bytes survived: the ciphertext for a symmetric key is a few hundred bytes long and identical every time you load it. If its length changes between where you saved it and where you use it, the bug is in storage, not KMS.
Envelope encryption: decrypt the key, not the data
KMS encrypts at most 4 KB directly. For files and larger data, you use envelope encryption: GenerateDataKey returns a plaintext data key (use it locally to encrypt the data, then discard it) and the same key encrypted under KMS (store it next to the data). To read the data back:
- Send the encrypted data key (a few hundred bytes) to KMS
Decrypt. - Use the returned plaintext data key locally to decrypt your file with AES-GCM.
- Discard the plaintext key from memory when done.
Sending the large encrypted file to KMS Decrypt instead of the small encrypted key gives InvalidCiphertextException (or a length error). If the data was encrypted with the AWS Encryption SDK or the S3 Encryption Client, those libraries wrap everything in their own message format, and only the same library can open it; KMS Decrypt cannot read that format at all.
Asymmetric KMS keys
With symmetric keys, the ciphertext records which key made it, so KeyId is optional on Decrypt. With asymmetric keys (RSA, SM2), it is not: you must pass the KeyId and the same EncryptionAlgorithm used to encrypt (for example RSAES_OAEP_SHA_256). Data encrypted outside AWS with the downloaded public key decrypts the same way, as long as the padding and hash match. A mismatch in algorithm produces InvalidCiphertextException.
kms.decrypt(CiphertextBlob=blob, KeyId="alias/partner-rsa", EncryptionAlgorithm="RSAES_OAEP_SHA_256")
When a partner encrypts with your downloaded public key, ask exactly which padding and hash they used: OpenSSL and most libraries default to different settings (PKCS#1 v1.5 padding, or OAEP with SHA-1), which KMS does not accept for decryption. Agree on OAEP with SHA-256 and test with one sample before the real data arrives. Asymmetric keys also limit plaintext size (a few hundred bytes for RSA), so they encrypt keys or short secrets, not files.
"The ciphertext refers to a customer master key that does not exist"
This message (an AccessDeniedException in KMS, and a 403 when it comes back through S3, Glue, Athena or EMR reading SSE-KMS objects) has three meanings, in order of likelihood:
- No permission: your role lacks
kms:Decrypton the key, or the key policy does not allow it, often because the key is in another account. Cross-account use needs both the key policy (allow the other account or role) and the caller's IAM policy. Our encrypted AMI guide shows a working cross-account key policy. - Wrong Region: KMS keys are Regional; the client is in a different Region from the key that encrypted the data (unless it is a multi-Region key with a replica there).
- The key is gone or unusable: deleted, pending deletion, or disabled. Check with
aws kms describe-key --key-id KEY_ARNin the key's Region.
For a Glue crawler or Athena query failing on S3 objects, the service's role is the one that needs kms:Decrypt on every key used by the objects in that bucket, including old objects written before a bucket's default key changed. Our S3 SSE-KMS AccessDenied guide covers the S3 side in detail, including bucket keys and cross-account buckets.
Jake: "The crawler fails only on the files from last year."
Ethan: "Those were written with the old key. The crawler role can decrypt the new key, not the old one. Add both."
When the policy looks right but Decrypt is still denied
Some key policies and IAM policies allow kms:Decrypt only under conditions, and a call that misses a condition is denied even though the action is listed. The common ones:
kms:ViaService: the key may only be used through a named service, such ass3.eu-west-2.amazonaws.com. Reading an S3 object works; callingDecrypton the key directly from your own code is denied, by design.kms:EncryptionContext:...conditions: the policy allows decryption only when the context contains certain pairs (for example a tenant ID). A call with a different or missing context is denied as an access error, before the ciphertext is even checked, so this shows up as AccessDenied rather than InvalidCiphertextException.kms:CallerAccountoraws:PrincipalOrgID: only callers from certain accounts or your organization may use the key.- Grants: AWS services such as EBS and RDS use grants to act on your behalf. A grant covers the service's use of the key, not yours, so your own role still needs its own permission.
Reading the key policy with aws kms get-key-policy --key-id KEY --policy-name default --output text shows these conditions; IAM Access Analyzer's policy checks can also explain which statement applies. When a policy uses conditions, the error type itself is a clue: AccessDenied points to a condition or permission, InvalidCiphertextException to the ciphertext or the context sent with it.
Decrypting with a key from another account, step by step
Cross-account decryption fails more often than any other KMS setup, because two policies in two accounts must agree. In order:
- In the key's account, add a statement to the key policy allowing the other account's role (or the whole account)
kms:Decrypt, pluskms:DescribeKeyif tools need to look the key up. - In the caller's account, give the role an IAM policy allowing
kms:Decrypton the key's full ARN. Both are required: the key policy alone does not grant a role in another account anything. - Use the key ARN, not an alias, whenever the caller names the key. Aliases only resolve inside the key's own account, so
alias/shop-datafrom another account looks like a key that does not exist. - Call the right Region: the client must be in the key's Region (or a multi-Region replica's Region).
- Check the key type: data under an AWS managed key such as
aws/s3can never be decrypted from another account; it needs re-encrypting first.
For data in S3 or other services, the same five steps apply to the role that reads through the service. Our encrypted AMI guide shows a complete working pair of policies.
The same fix in Node.js, Java and .NET
Every SDK has the same two knobs, the ciphertext bytes and the encryption context. In Node.js with the AWS SDK v3:
import { KMSClient, DecryptCommand } from "@aws-sdk/client-kms";
const kms = new KMSClient({ region: "eu-west-2" });
const out = await kms.send(new DecryptCommand({
CiphertextBlob: Buffer.from(process.env.PAYMENT_API_KEY, "base64"),
EncryptionContext: { LambdaFunctionName: process.env.AWS_LAMBDA_FUNCTION_NAME },
}));
const apiKey = Buffer.from(out.Plaintext).toString("utf8");
In Java (SDK v2), build a DecryptRequest with SdkBytes.fromByteArray(Base64.getDecoder().decode(text)) and .encryptionContext(Map.of("LambdaFunctionName", name)); in .NET, set CiphertextBlob to a MemoryStream over the decoded bytes and fill the EncryptionContext dictionary. The traps are identical across languages: decode base64 once, keep the bytes as bytes (not a string), and send the same context map that was used to encrypt.
One more habit that applies in every language, and especially in Lambda: decrypt once and keep the result. Do the decrypt at module level, or on the first call, and store the plaintext in memory for the life of the process or execution environment. A function that decrypts the same API key on every invocation pays for every call, adds latency to every request, and can run into KMS request quotas under load, all for a value that has not changed.
Decrypt less: cost, quotas and caching
Every KMS request is billed (about $0.03 per 10,000 requests for symmetric operations) and counts against a per-Region request quota shared by everything in the account. Errors and bills both drop when code decrypts less often:
- Cache decrypted secrets in memory for the life of the process, as above.
- Use data key caching in the AWS Encryption SDK, which reuses a data key for many messages within limits you set, instead of calling KMS for each one.
- Turn on S3 Bucket Keys for SSE-KMS buckets, which cut the KMS calls S3 makes for objects in that bucket dramatically.
- Read secrets through a cache: the Secrets Manager caching libraries and the Lambda Parameters and Secrets extension avoid a Secrets Manager call (and its KMS call) on every request.
When a busy service suddenly shows ThrottlingException from KMS after a deploy, the cause is almost always a change that moved a decrypt into a hot path. Our KMS costs guide works through the numbers.
Imported key material, custom and external key stores
Most keys use key material that KMS generates. Two less common setups add their own failure modes:
- Imported key material: you bring your own key material, optionally with an expiry date. When it expires or is deleted, the key enters a pending-import state and every decrypt fails with
KMSInvalidStateExceptionuntil you import the same material again. Keep a safe copy of imported material, and track expiry dates as carefully as certificates. - Custom key stores backed by CloudHSM and external key stores backed by your own key manager outside AWS: if the cluster or the external system is unreachable or overloaded, calls fail with
KeyUnavailableExceptionor time out. These are infrastructure problems, not ciphertext problems; check the key store's connection state and the health of the system behind it.
Neither setup changes the encryption context or encoding rules; they only add the possibility that the key itself is temporarily or permanently out of reach.
Multi-Region keys and decrypting in another Region
A standard KMS key exists in one Region, and ciphertext from it can only be decrypted there. Multi-Region keys are the exception: a primary key and its replicas in other Regions share the same key material and key ID (they start with mrk-), so data encrypted in one Region can be decrypted in another without a call back to the original Region.
- You cannot convert an existing single-Region key into a multi-Region key. To decrypt old data elsewhere, decrypt it in its home Region, or move it under a new multi-Region key with
ReEncrypt. - Each replica has its own key policy, so a role allowed on the primary is not automatically allowed on the replica.
- Disaster recovery plans often assume data can be read in the second Region; if the key is single-Region, that assumption fails exactly when you need it. Check before the drill, not during.
Key rotation and re-encrypting data
Rotation worries people, but it does not cause InvalidCiphertextException. With automatic rotation, KMS keeps every older version of the key material, so ciphertext from before a rotation still decrypts with the same key ID; only new encryptions use the new material. Rotation periods can be set (from 90 days up to several years), and you can also rotate on demand.
Moving data to a different key is a separate job. ReEncrypt decrypts the ciphertext under the old key and encrypts it under a new one inside KMS, so the plaintext never leaves the service. It is the tool for moving data to a multi-Region key, to a key in a different account's control, or away from a key you plan to delete. For S3 objects, a copy in place with the new key (or an S3 Batch Operations copy job) re-encrypts every object, since S3 encrypts each object's data key under the bucket's chosen KMS key.
Jake: "We rotated the key last month. Is that why old records fail?"
Ethan: "No. Rotation keeps the old material for old ciphertext. Something else changed: look at the context."
A key pending deletion, disabled, or deleted
- Pending deletion (a 7 to 30 day waiting period): decrypt fails with
KMSInvalidStateException. Cancel the deletion withaws kms cancel-key-deletion --key-id KEY, then enable the key; the data is safe again. Set a CloudWatch alarm on attempts to use keys pending deletion, so you learn about this during the waiting period, not after. - Disabled:
DisabledException; enable the key. - Deleted: the key material is gone, and everything encrypted only under that key is permanently unreadable, by you and by AWS. There is no recovery path, which is the whole reason for the waiting period.
- Imported key material expired or deleted:
KMSInvalidStateException; re-import the same key material to restore access.
When the KMS error comes from another AWS service
Often you never call KMS yourself: another service does, and reports the KMS failure with a message of its own. The messages differ, the causes are the same:
| Where you see it | Typical message | What to check |
|---|---|---|
| Lambda | Unable to decrypt the environment variables because KMS access was denied | The function's execution role needs kms:Decrypt on the key used for its environment variables |
| Secrets Manager | Access to KMS is not allowed | The caller needs kms:Decrypt on the secret's key, as well as secretsmanager:GetSecretValue |
| Parameter Store | AccessDenied on a SecureString read with decryption | kms:Decrypt on the parameter's key; reading without decryption works, which confirms it |
| S3, Glue, Athena | 403 Access Denied on SSE-KMS objects | kms:Decrypt for every key the objects use, including old and cross-account keys |
| EC2 and EBS | Instance stops right after start, or volume errors naming the KMS key | The key is disabled or pending deletion, or the role launching the instance cannot use it |
| RDS | Database in an "inaccessible encryption credentials" state | The key was disabled or its policy changed; restore access, then start the database |
In every row, CloudTrail shows the underlying KMS call made by the service on your behalf, with its error, which tells you whether it is a permission, a key state or a Region problem. Fix the key or the permission, not the service.
Parameter Store SecureString parameters
SecureString parameters in Systems Manager Parameter Store are encrypted with a KMS key, by default the AWS managed aws/ssm key. Reading one with decryption needs two permissions: ssm:GetParameter on the parameter, and kms:Decrypt on its key. A quick test separates the two: aws ssm get-parameter --name /shop/payment-key without --with-decryption returns the encrypted value if the SSM permission is fine, so a failure only when you add decryption points squarely at KMS.
Two follow-on rules matter. Parameters under aws/ssm cannot be read from another account, for the same reason as every AWS managed key; use a customer managed key for shared parameters. And you never call KMS Decrypt on a SecureString value yourself: Parameter Store uses its own encryption context, so asking Parameter Store to decrypt (with --with-decryption or WithDecryption=True) is the only route that works.
AWS managed keys vs customer managed keys
Which kind of key encrypted your data decides what you can fix:
- AWS managed keys (aliases like
aws/s3,aws/lambda,aws/secretsmanager) are created by services in your account. Their key policies cannot be edited, so they can never be shared with another account, and only the service can use them on your behalf. Cross-account errors on data under these keys cannot be fixed with policies; re-encrypt the data under a key you control. - Customer managed keys are yours: editable key policies, cross-account sharing, rotation settings, deletion when you choose. They cost about $1 a month each plus request charges, which is a small price for the control.
- AWS owned keys belong to the service and are invisible to you; data under them is decrypted only through the service, and you never see KMS errors about them.
For anything shared across accounts or read by several services, start with a customer managed key; switching later means re-encrypting existing data.
Habits that prevent these errors in new code
- Store the encryption context with the data, or derive it from something stable (a record ID, a tenant ID), so whoever decrypts later knows exactly what to send.
- Store ciphertext as standard base64 text in one place, and decode it once, at the edge of your code, right before calling KMS.
- Record the key ARN next to the ciphertext. For symmetric keys it is not required, but it makes Region and account questions trivial to answer later.
- Use a library for anything over 4 KB: the AWS Encryption SDK handles envelope encryption, data key caching and contexts correctly, and its messages carry everything needed to decrypt.
- Alert on keys scheduled for deletion: an EventBridge rule on the
ScheduleKeyDeletioncall, sent to your team, turns an irreversible mistake into a reversible one. - Prefer managed secret stores (Secrets Manager, Parameter Store) over hand-encrypted environment variables, so services handle KMS for you.
When the data really is unrecoverable
Most InvalidCiphertextException cases end happily, but a few do not, and it is better to recognize them early:
- The ciphertext bytes were truncated or rewritten, for example by a text column that dropped bytes, and no other copy exists. KMS cannot repair ciphertext; recover the original from a backup of the source, or re-create the secret.
- The encryption context is unknown, the code that encrypted is gone, and the Encrypt events are older than any CloudTrail record you kept (Event history holds 90 days; trails keep whatever you stored). Guessing context values is possible in principle but rarely practical.
- The key was deleted after its waiting period. Nothing, including AWS, can decrypt data encrypted only under it.
For secrets, the practical recovery is to issue a new one (a new API key from the provider, a new password) and store it properly this time. For business data, it is the backup. That is why the prevention habits above, recording the context, keeping a trail, and alerting on key deletion, are worth the half hour they take to set up.
Jake: "So the old encrypted export from 2023 is just gone?"
Ethan: "Its key was deleted last year. Yes. That's why deletion alerts go to the whole team now."
Every KMS error code, explained
| Error code | What it means for you |
|---|---|
InvalidCiphertextException | Ciphertext, encryption context or algorithm does not match (this page) |
IncorrectKeyException | You named a KeyId that is not the key that encrypted this ciphertext |
AccessDeniedException | IAM or key policy does not allow the call, including "ciphertext refers to a key that does not exist" |
NotFoundException | The key ID, alias or ARN does not exist in this Region |
DisabledException | The key is disabled |
KMSInvalidStateException | The key is pending deletion, pending import, or otherwise not usable right now |
InvalidKeyUsageException | The key's usage does not fit the operation (a signing key used to encrypt, for example) |
KeyUnavailableException | The key material is temporarily unavailable (common with custom or external key stores); retry |
DependencyTimeoutException | A KMS dependency timed out; retry with backoff |
KMSInternalException | An internal KMS error; retry |
ThrottlingException ("Rate exceeded") | You exceeded the per-Region request quota; cache data keys, enable S3 Bucket Keys, or request more |
InvalidGrantTokenException | A grant token in the request is malformed or not valid |
LimitExceededException | A resource quota was hit (too many keys, aliases or grants) |
DryRunOperationException | Not a failure: the call was a dry run and would have succeeded |
To branch on these in Python, read error.response["Error"]["Code"]; our ClientError guide shows the pattern. Throttling also has a cost angle: every KMS request is billed, so the fixes for throttling usually cut the bill too; see our KMS costs guide.
Frequently asked questions
What does InvalidCiphertextException mean in AWS KMS?
KMS cannot decrypt the ciphertext as given, usually because the encryption context differs, the bytes were altered, or the wrong blob was sent.
How do I fix InvalidCiphertextException?
Pass the exact encryption context used at encryption, decode base64 exactly once, send the encrypted data key rather than the data, and use the same algorithm for asymmetric keys.
Why does my Lambda environment variable fail to decrypt?
Console-encrypted variables use the encryption context LambdaFunctionName; pass it with your function's name and base64-decode the value first.
What is a KMS encryption context?
Non-secret key-value pairs bound to ciphertext at encryption; decryption must supply exactly the same pairs.
What does "the ciphertext refers to a customer master key that does not exist" mean?
An access problem: no kms:Decrypt permission, the key is in another account or Region, or it was deleted or disabled.
What is the difference between InvalidCiphertextException and IncorrectKeyException?
IncorrectKeyException means you named the wrong KeyId; InvalidCiphertextException means the ciphertext, context or algorithm does not match.
Can I decrypt data after a KMS key is deleted?
No. Data encrypted only under a deleted key is permanently unreadable; cancel deletion during the waiting period instead.
How do I cancel a KMS key deletion?
Run aws kms cancel-key-deletion --key-id KEY during the 7 to 30 day waiting period, then enable the key.
Why does Glue or Athena get 403 on S3 objects encrypted with KMS?
The service role lacks kms:Decrypt on the key (or keys) that encrypted those objects, often an older key or one in another account.
Can I decrypt KMS ciphertext in another Region?
Only with a multi-Region key that has a replica in that Region; otherwise decrypt in the key's own Region.
Why does KMS Decrypt fail on a file encrypted with the Encryption SDK?
The Encryption SDK uses its own message format; decrypt it with the same SDK, not with KMS Decrypt.
How do I decrypt with an asymmetric KMS key?
Pass KeyId and the same EncryptionAlgorithm, such as RSAES_OAEP_SHA_256, used to encrypt.
What is the size limit for KMS Encrypt?
4 KB of plaintext; use envelope encryption with GenerateDataKey for larger data.
What does KMSInvalidStateException mean?
The key is not in a usable state, typically pending deletion or with imported key material missing.
What does KMS ThrottlingException Rate exceeded mean?
Your requests exceeded the Region's KMS quota; cache data keys, use S3 Bucket Keys, or request a higher quota.
How do I find the encryption context used to encrypt data?
Look up the Encrypt or GenerateDataKey event in CloudTrail; the context is in the request parameters.
Do I need KeyId to decrypt with a symmetric KMS key?
No, the ciphertext identifies the key; naming it is optional but must then be correct.
Why does aws kms decrypt fail from the CLI?
Usually blob encoding: pass binary with fileb:// or base64 text directly, not base64 text through fileb://.
Is the KMS encryption context secret?
No. It is stored in plain text in CloudTrail, which makes it useful for auditing; keep secrets out of it.
What should I use as an encryption context?
Short, stable values you always know when decrypting, such as a tenant or record ID; avoid anything that changes, like timestamps or renamable names.
Why does get-parameter fail only with --with-decryption?
Your role can read the parameter but not decrypt it; grant kms:Decrypt on the SecureString's KMS key.
Can I decrypt a SecureString value with KMS directly?
No. Parameter Store uses its own encryption context; ask Parameter Store to decrypt with --with-decryption instead.
How do I reduce KMS ThrottlingException errors?
Decrypt once and cache the result, use data key caching in the Encryption SDK, enable S3 Bucket Keys, and read secrets through a caching library.
What happens when imported key material expires?
The key becomes unusable and decrypts fail with KMSInvalidStateException until you import the same key material again.
What does KeyUnavailableException mean?
The key material is temporarily unreachable, usually because a CloudHSM or external key store is disconnected or overloaded; retry and check the key store.
Can corrupted KMS ciphertext be repaired?
No. Recover the original from a backup or re-create the secret; KMS cannot fix altered ciphertext.
Why are KMS error messages so vague?
On purpose: detailed reasons would help an attacker probing your data, so KMS returns short codes and you check bytes, context, key, Region and permissions yourself.
Can I use a KMS alias from another account?
No. Aliases resolve only in the key's own account; use the full key ARN when calling from another account.
What permissions does cross-account KMS decryption need?
The key policy must allow the other account's role, and that role's IAM policy must allow kms:Decrypt on the key ARN.
How do I decrypt KMS ciphertext in Node.js?
Use DecryptCommand from @aws-sdk/client-kms with the base64-decoded bytes as CiphertextBlob and the same EncryptionContext used to encrypt.
What does "Access to KMS is not allowed" mean in Secrets Manager?
The caller can read the secret but not decrypt it; grant kms:Decrypt on the secret's KMS key.
Why can't my Lambda function decrypt its environment variables?
The execution role lacks kms:Decrypt on the key used for the function's environment variables, or that key is disabled.
Can I share an AWS managed key like aws/s3 with another account?
No. AWS managed key policies cannot be edited; re-encrypt the data under a customer managed key to share it.
How much does a customer managed KMS key cost?
About $1 per key per month, plus request charges for each encrypt and decrypt call.
How do I get alerted when someone schedules a KMS key for deletion?
Create an EventBridge rule for the ScheduleKeyDeletion API call and send it to SNS or your chat tool.
How do I base64-decode KMS output in PowerShell?
Use [Convert]::FromBase64String on the trimmed text, and write ciphertext files with [IO.File]::WriteAllBytes rather than Out-File.
How do I see what encryption context my Decrypt call sent?
Find the failed Decrypt event in CloudTrail; the request parameters show the context you sent, to compare with the Encrypt event.
Why is KMS Decrypt denied when my policy allows it?
A condition such as kms:ViaService, an encryption context condition, or an account condition is not met by your call.
Does KMS key rotation break decryption of old data?
No. Automatic rotation keeps old key material, so older ciphertext still decrypts under the same key ID.
Can I convert a KMS key to a multi-Region key?
No. Create a new multi-Region key and move data to it with ReEncrypt.
What does KMS ReEncrypt do?
It decrypts ciphertext under one key and encrypts it under another inside KMS, without exposing the plaintext.
Can I decrypt with a multi-Region key replica?
Yes. Replicas share key material with the primary, so ciphertext from one Region decrypts in another, if the replica's key policy allows you.
For readers preparing for an AWS certification
We have been covering AWS certifications alongside our fixes, so here is a short extra for readers studying for one. If you only came for the fix, you already have it; feel free to skip this part.
Exams that test this topic: Security – Specialty (SCS-C03) • Developer – Associate (DVA-C03 from December 1, 2026) • CloudOps Engineer – Associate (SOA-C03).
Here are the exam-style questions this topic tends to raise, with the reasoning you need to answer them:
1. Data encrypted with KMS using an encryption context fails to decrypt with InvalidCiphertextException. What is the likely cause?
Decrypt was called without the exact same encryption context.
2. What is the maximum plaintext size for a direct KMS Encrypt call, and what do you use for larger data?
4 KB; use envelope encryption with GenerateDataKey.
3. A KMS key was scheduled for deletion by mistake. How can the encrypted data be saved?
Cancel the key deletion during the waiting period (7 to 30 days) and re-enable the key.
4. What is needed for a role in account B to decrypt data encrypted with a KMS key in account A?
The key policy in account A must allow it, and the role's IAM policy must allow kms:Decrypt on the key.
5. Which feature reduces KMS request costs and throttling for S3 SSE-KMS buckets?
S3 Bucket Keys.
6. Can a single-Region KMS key decrypt data in another AWS Region?
No; only multi-Region keys have replicas in other Regions.
Exam codes and dates change often. Our AWS certification changes page keeps track of what is current.
Jake's function now passes the LambdaFunctionName context and decrypts on the first try. Ethan moved the payment key into Secrets Manager a week later anyway, and added CloudWatch alarms for any KMS key scheduled for deletion, after reading the section on what deleted keys mean.
The team's runbook for "KMS says invalid" is now four lines long: compare the ciphertext length, read the failed Decrypt event in CloudTrail, compare its encryption context with the Encrypt event, and check the key's state and Region. In the months since, every case has ended by line three. The fourth line has only been needed once, for a key someone had disabled while testing, and the deletion alert caught a second near-miss a week before it could become permanent. Both took minutes to fix.
📌 If you keep one line from this page
InvalidCiphertextException usually means the encryption context, encoding or blob differs from what was encrypted; "ciphertext refers to a key that does not exist" means permission, Region or a deleted key; and a key pending deletion can still be saved with cancel-key-deletion.
LambdaFunctionName for console-encrypted variables · decode base64 once · decrypt the data key, not the data.
Revision note. Written October 20, 2026. Most "invalid" ciphertext is valid; the question asked of it is slightly different.