apache/iceberg · error · RuntimeException

Failed to encrypt

Error message

Failed to encrypt

What it means

Post-condition failure in Ciphers.encrypt: after AES-GCM doFinal wrote into the ciphertext buffer (with room left for the nonce), the number of bytes produced does not equal plaintextLength + GCM tag length. This is a defensive sanity check on JCE cipher output — it cannot be triggered by plaintext content and indicates corrupted cipher state or a JVM crypto mismatch.

Solutions

  1. Check the wrapped cause (getCause()) for the real GeneralSecurityException
  2. Ensure the key is exactly 16, 24, or 32 bytes
  3. Upgrade to a modern JDK without export crypto restrictions
  4. Verify a functioning AES/GCM provider is installed

Example fix

// inspect the real cause
try { ... } catch (RuntimeException e) {
  log.error("encrypt failed", e.getCause());
}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check key material
byte[] key = keySelector.keyFor(file);
if (key == null || !(key.length == 16 || key.length == 24 || key.length == 32)) {
  throw new IllegalArgumentException("AES key must be 16/24/32 bytes, got " + (key == null ? "null" : key.length));
}

Try / catch

try {
  cipherOut.write(data);
} catch (RuntimeException e) {
  if ("Failed to encrypt".equals(e.getMessage())) {
    Throwable cause = e.getCause(); // GeneralSecurityException with the real reason
    throw new IllegalStateException("Encryption failed: " + cause, e);
  }
  throw e;
}

Prevention

When it happens

Trigger: Cipher.getInstance/init/doFinal throwing GeneralSecurityException during encrypt() — e.g. an invalid AES key length, a restricted (crypto-policy-limited) JVM, or provider initialization failure.

Common situations: Older JDKs with limited-strength crypto policy and 256-bit keys; a key that isn't a valid AES key size (16/24/32 bytes); missing JCE unlimited-strength policy files on legacy Java 8.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/ab0f1e0a889c6e94. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/apache/iceberg/encryption/Ciphers.java:114

        // doFinal encrypts and adds a GCM tag. The nonce is added later.
        enciphered =
            cipher.doFinal(
                plaintext,
                plaintextOffset,
                plaintextLength,
                ciphertextBuffer,
                ciphertextOffset + NONCE_LENGTH);

        if (enciphered != plaintextLength + GCM_TAG_LENGTH) {
          throw new RuntimeException(
              "Failed to encrypt block: expected "
                  + plaintextLength
                  + GCM_TAG_LENGTH
                  + " encrypted bytes but produced bytes "
                  + enciphered);
        }
      } catch (GeneralSecurityException e) {
        throw new RuntimeException("Failed to encrypt", e);
      }

      // Add the nonce
      System.arraycopy(nonce, 0, ciphertextBuffer, ciphertextOffset, NONCE_LENGTH);

      return enciphered + NONCE_LENGTH;
    }
  }

  public static class AesGcmDecryptor {
    private final SecretKeySpec aesKey;
    private final Cipher cipher;

    public AesGcmDecryptor(byte[] keyBytes) {
      this.aesKey = newKey(keyBytes);
      this.cipher = newCipher();
    }

View on GitHub (pinned to 86d9c8fc54)