{"record":{"id":"578a21d5fc6a8489","repo":"apache/iceberg","slug":"failed-to-decrypt","errorCode":null,"errorMessage":"Failed to decrypt","messagePattern":"Failed to decrypt","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/encryption/Ciphers.java","lineNumber":181,"sourceCode":"        cipher.init(Cipher.DECRYPT_MODE, aesKey, spec);\n        if (null != aad) {\n          cipher.updateAAD(aad);\n        }\n        // For java Cipher, the nonce is not part of ciphertext\n        plaintextLength =\n            cipher.doFinal(\n                ciphertext,\n                ciphertextOffset + NONCE_LENGTH,\n                ciphertextLength - NONCE_LENGTH,\n                plaintextBuffer,\n                plaintextOffset);\n      } catch (AEADBadTagException e) {\n        throw new RuntimeException(\n            \"GCM tag check failed. Possible reasons: wrong decryption key; or corrupt/tampered\"\n                + \" data. AES GCM doesn't differentiate between these two.\",\n            e);\n      } catch (GeneralSecurityException e) {\n        throw new RuntimeException(\"Failed to decrypt\", e);\n      }\n\n      return plaintextLength;\n    }\n  }\n\n  private static SecretKeySpec newKey(byte[] keyBytes) {\n    Preconditions.checkArgument(keyBytes != null, \"Invalid key: null\");\n    int keyLength = keyBytes.length;\n    Preconditions.checkArgument(\n        (keyLength == 16 || keyLength == 24 || keyLength == 32),\n        \"Invalid key length: %s (must be 16, 24, or 32 bytes)\",\n        keyLength);\n    return new SecretKeySpec(keyBytes, \"AES\");\n  }\n\n  private static Cipher newCipher() {\n    try {","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/encryption/Ciphers.java#L163-L199","documentation":"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.","triggerScenarios":"Cipher init/update/doFinal during decrypt() throwing GeneralSecurityException — typically invalid key material (wrong AES key size), invalid algorithm parameters, or provider failure.","commonSituations":"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).","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"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (key == null || !(key.length == 16 || key.length == 24 || key.length == 32)) {\n  throw new IllegalArgumentException(\"invalid AES key length: \" + (key == null ? \"null\" : key.length));\n}","typeGuard":null,"tryCatchPattern":"try {\n  cipher.decrypt(...);\n} catch (RuntimeException e) {\n  if (\"Failed to decrypt\".equals(e.getMessage())) {\n    log.error(\"decrypt failed, cause: {}\", e.getCause());\n  }\n  throw e;\n}","preventionTips":["Validate key length and base64 decoding before decrypting","Confirm the KMS returns the expected raw data key format","Keep JDK crypto policy unrestricted"],"tags":["encryption","crypto","jce"],"backgroundTag":"decryption-failed","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}