apache/iceberg · error · RuntimeException
Failed to decrypt
Error message
Failed to decrypt
What it means
Ciphers.decrypt wraps any GeneralSecurityException other than the bad-tag case (which gets its own message) in a RuntimeException('Failed to decrypt') with the original exception chained as the cause.
Solutions
- Inspect getCause() for the underlying GeneralSecurityException
- Validate that the key is 16/24/32 bytes and correctly decoded (single base64 decode)
- Upgrade/fix the JDK security providers
Defensive patterns
Strategy: try-catch
Validate before calling
if (key == null || !(key.length == 16 || key.length == 24 || key.length == 32)) {
throw new IllegalArgumentException("invalid AES key length: " + (key == null ? "null" : key.length));
} Try / catch
try {
cipher.decrypt(...);
} catch (RuntimeException e) {
if ("Failed to decrypt".equals(e.getMessage())) {
log.error("decrypt failed, cause: {}", e.getCause());
}
throw e;
} Prevention
- Validate key length and base64 decoding before decrypting
- Confirm the KMS returns the expected raw data key format
- Keep JDK crypto policy unrestricted
When it happens
Trigger: Cipher init/update/doFinal during decrypt() throwing GeneralSecurityException — typically invalid key material (wrong AES key size), invalid algorithm parameters, or provider failure.
Common situations: Passing a raw envelope data key of the wrong length; a restricted JCE policy JVM; corrupted key bytes decoded from KMS with wrong encoding (e.g. base64 decoded twice).
Related errors
- Failed to encrypt
- Failed to create GCM cipher
- Failed to encrypt block: expected
- GCM tag check failed. Possible reasons: wrong decryption…
- Avro does not support AAD prefix
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/578a21d5fc6a8489.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/org/apache/iceberg/encryption/Ciphers.java:181
cipher.init(Cipher.DECRYPT_MODE, aesKey, spec);
if (null != aad) {
cipher.updateAAD(aad);
}
// For java Cipher, the nonce is not part of ciphertext
plaintextLength =
cipher.doFinal(
ciphertext,
ciphertextOffset + NONCE_LENGTH,
ciphertextLength - NONCE_LENGTH,
plaintextBuffer,
plaintextOffset);
} catch (AEADBadTagException e) {
throw new RuntimeException(
"GCM tag check failed. Possible reasons: wrong decryption key; or corrupt/tampered"
+ " data. AES GCM doesn't differentiate between these two.",
e);
} catch (GeneralSecurityException e) {
throw new RuntimeException("Failed to decrypt", e);
}
return plaintextLength;
}
}
private static SecretKeySpec newKey(byte[] keyBytes) {
Preconditions.checkArgument(keyBytes != null, "Invalid key: null");
int keyLength = keyBytes.length;
Preconditions.checkArgument(
(keyLength == 16 || keyLength == 24 || keyLength == 32),
"Invalid key length: %s (must be 16, 24, or 32 bytes)",
keyLength);
return new SecretKeySpec(keyBytes, "AES");
}
private static Cipher newCipher() {
try {View on GitHub (pinned to 86d9c8fc54)