{"record":{"id":"bf9ca2b1f42f4315","repo":"apache/pulsar","slug":"outbuffer-has-not-enough-space-available","errorCode":null,"errorMessage":"Outbuffer has not enough space available","messagePattern":"Outbuffer has not enough space available","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-client-messagecrypto-bc/src/main/java/org/apache/pulsar/client/impl/crypto/MessageCryptoBc.java","lineNumber":513,"sourceCode":"        }\n\n        // Create gcm param\n        // TODO: Replace random with counter and periodic refreshing based on timer/counter value\n        byte[] iv = new byte[IV_LEN];\n        secureRandom.nextBytes(iv);\n        GCMParameterSpec gcmParam = new GCMParameterSpec(tagLen, iv);\n\n        // Update message metadata with encryption param\n        msgMetadata.setEncryptionParam(iv);\n\n        try {\n            // Encrypt the data\n            Cipher cipher = getAesGcmCipher();\n            cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, gcmParam);\n\n            int maxLength = cipher.getOutputSize(payload.remaining());\n            if (outBuffer.remaining() < maxLength) {\n                throw new IllegalArgumentException(\"Outbuffer has not enough space available\");\n            }\n\n            int bytesStored = cipher.doFinal(payload, outBuffer);\n            outBuffer.flip();\n            outBuffer.limit(bytesStored);\n        } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException\n                | InvalidAlgorithmParameterException | ShortBufferException e) {\n            log.error().attr(\"logCtx\", logCtx).exception(e).log(\"Failed to encrypt message\");\n            throw new PulsarClientException.CryptoException(e.getMessage());\n        }\n    }\n\n    private SecretKeySpec tryDecryptDataKey(String keyName, byte[] encryptedDataKey, List<KeyValue> encKeyMeta,\n            CryptoKeyReader keyReader) {\n        Map<String, String> keyMeta = new HashMap<String, String>();\n        encKeyMeta.forEach(kv -> {\n            keyMeta.put(kv.getKey(), kv.getValue());\n        });","sourceCodeStart":495,"sourceCodeEnd":531,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-client-messagecrypto-bc/src/main/java/org/apache/pulsar/client/impl/crypto/MessageCryptoBc.java#L495-L531","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Size the out buffer as payload size plus at least 16 bytes (GCM tag), e.g. ByteBuffer.allocate(payload.remaining() + 16)","Call the cipher's getOutputSize(payloadLen) yourself before allocating and clear/compact the buffer before reuse","If reusing buffers, ensure outBuffer.clear() is called so remaining() reflects full capacity","Verify the payload passed is not unexpectedly larger than the buffer allocation (e.g. chunking configured off with large messages)"],"exampleFix":"// before\nByteBuffer out = ByteBuffer.allocate(payload.remaining()); // misses GCM tag\n// after\nByteBuffer out = ByteBuffer.allocate(payload.remaining() + 16); // 16-byte GCM tag headroom","handlingStrategy":"validation","validationCode":"// Size the output buffer before calling encrypt\nint outSize = payload.remaining() + 16; // payload + GCM tag\nByteBuffer outBuffer = ByteBuffer.allocate(outSize);\nif (outBuffer.remaining() < outSize) {\n    throw new IllegalStateException(\"encrypt output buffer too small\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    crypto.encrypt(encryptionKeyInfo, payload, outBuffer);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"Outbuffer\")) {\n        outBuffer = ByteBuffer.allocate(payload.remaining() + 16);\n        crypto.encrypt(encryptionKeyInfo, payload, outBuffer);\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always allocate encryption output as payload size + 16 bytes for the GCM tag","Call buffer.clear() before reusing pooled ByteBuffers","Compute cipher.getOutputSize(payloadLen) instead of guessing buffer sizes","Add a unit test that encrypts max-size messages with production-sized buffers"],"tags":["crypto","aes-gcm","buffer","bytebuffer","argument-validation"],"backgroundTag":"buffer-too-small","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}