apache/iceberg · error · RuntimeException

Failed to encrypt block: expected

Error message

Failed to encrypt block: expected ${plaintextLength} encrypted bytes but produced bytes ${enciphered}

What it means

AfterCipher.doFinal, Ciphers.encrypt verifies the cipher produced exactly plaintextLength + GCM_TAG_LENGTH bytes (ciphertext plus 16-byte auth tag). Any other count means the JCE provider misbehaved, so it throws RuntimeException. Note the message itself has a string-concatenation bug: the tag length is appended directly to the number rather than summed.

Solutions

  1. Verify the JVM's security providers; remove or fix any custom JCE provider overriding AES/GCM
  2. Run on a standard, up-to-date JDK
  3. Report to the provider vendor if a third-party provider is required
Defensive patterns

Strategy: try-catch

Try / catch

try {
  cipherOut.write(data);
} catch (RuntimeException e) {
  if (e.getMessage() != null && e.getMessage().startsWith("Failed to encrypt block")) {
    // JCE provider bug: log provider info and fail fast
    log.error("Provider: {}", Security.getProviders());
  }
  throw e;
}

Prevention

When it happens

Trigger: A JCE provider returning an unexpected output length from AES/GCM/NoPadding doFinal during encrypt() — essentially never with standard JDK providers.

Common situations: Non-standard or buggy third-party security providers; custom JVM crypto instrumentation; running on an unusual JDK with a defective GCM implementation.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

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

      try {
        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH_BITS, nonce);
        cipher.init(Cipher.ENCRYPT_MODE, aesKey, spec);
        if (null != aad) {
          cipher.updateAAD(aad);
        }

        // 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 {

View on GitHub (pinned to 86d9c8fc54)