apache/pulsar · error · IllegalArgumentException

Outbuffer has not enough space available

Error message

Outbuffer has not enough space available

What it means

MessageCryptoBc.encrypt computes cipher.getOutputSize(payload.remaining()) (ciphertext + 16-byte GCM tag) and requires the caller-supplied outBuffer to have at least that many bytes remaining. This guards against a silent BufferOverflowException from cipher.doFinal(payload, outBuffer); when the destination ByteBuffer is too small it throws IllegalArgumentException instead.

Source

Thrown at pulsar-client-messagecrypto-bc/src/main/java/org/apache/pulsar/client/impl/crypto/MessageCryptoBc.java:513

        }

        // Create gcm param
        // TODO: Replace random with counter and periodic refreshing based on timer/counter value
        byte[] iv = new byte[IV_LEN];
        secureRandom.nextBytes(iv);
        GCMParameterSpec gcmParam = new GCMParameterSpec(tagLen, iv);

        // Update message metadata with encryption param
        msgMetadata.setEncryptionParam(iv);

        try {
            // Encrypt the data
            Cipher cipher = getAesGcmCipher();
            cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, gcmParam);

            int maxLength = cipher.getOutputSize(payload.remaining());
            if (outBuffer.remaining() < maxLength) {
                throw new IllegalArgumentException("Outbuffer has not enough space available");
            }

            int bytesStored = cipher.doFinal(payload, outBuffer);
            outBuffer.flip();
            outBuffer.limit(bytesStored);
        } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException
                | InvalidAlgorithmParameterException | ShortBufferException e) {
            log.error().attr("logCtx", logCtx).exception(e).log("Failed to encrypt message");
            throw new PulsarClientException.CryptoException(e.getMessage());
        }
    }

    private SecretKeySpec tryDecryptDataKey(String keyName, byte[] encryptedDataKey, List<KeyValue> encKeyMeta,
            CryptoKeyReader keyReader) {
        Map<String, String> keyMeta = new HashMap<String, String>();
        encKeyMeta.forEach(kv -> {
            keyMeta.put(kv.getKey(), kv.getValue());
        });

View on GitHub (pinned to 820761864e)

Solutions

  1. Size the out buffer as payload size plus at least 16 bytes (GCM tag), e.g. ByteBuffer.allocate(payload.remaining() + 16)
  2. Call the cipher's getOutputSize(payloadLen) yourself before allocating and clear/compact the buffer before reuse
  3. If reusing buffers, ensure outBuffer.clear() is called so remaining() reflects full capacity
  4. Verify the payload passed is not unexpectedly larger than the buffer allocation (e.g. chunking configured off with large messages)

Example fix

// before
ByteBuffer out = ByteBuffer.allocate(payload.remaining()); // misses GCM tag
// after
ByteBuffer out = ByteBuffer.allocate(payload.remaining() + 16); // 16-byte GCM tag headroom
Defensive patterns

Strategy: validation

Validate before calling

// Size the output buffer before calling encrypt
int outSize = payload.remaining() + 16; // payload + GCM tag
ByteBuffer outBuffer = ByteBuffer.allocate(outSize);
if (outBuffer.remaining() < outSize) {
    throw new IllegalStateException("encrypt output buffer too small");
}

Try / catch

try {
    crypto.encrypt(encryptionKeyInfo, payload, outBuffer);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("Outbuffer")) {
        outBuffer = ByteBuffer.allocate(payload.remaining() + 16);
        crypto.encrypt(encryptionKeyInfo, payload, outBuffer);
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling MessageCryptoBc.encrypt(encryptionKeyInfo, chunkMessageBuffer, outBuffer) with an outBuffer whose remaining() capacity is smaller than payload size + GCM tag overhead (16 bytes) + any cipher block padding overhead.

Common situations: Reusing a pooled/rotated buffer that was sized for a smaller payload; forgetting that the output includes the 16-byte authentication tag; a producer chunked-message buffer allocated once and reused across differently sized messages.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/bf9ca2b1f42f4315. Report an issue: GitHub.