apache/cassandra · error · IllegalStateException
could not read encrypted blocked metadata header
Error message
could not read encrypted blocked metadata header
What it means
EncryptionUtils.decrypt reads the fixed-size encrypted block metadata header (two ints: encrypted and plaintext lengths) from the channel. If the channel yields fewer bytes than ENCRYPTED_BLOCK_HEADER_SIZE, the data is truncated/corrupt and IllegalStateException is thrown.
Solutions
- Treat the segment as truncated: verify file size against the segment's recorded sync position / commit log metadata.
- Restore the segment from backup or rely on unflushed data loss handling (commit logs are restart-safe; partial tails are skipped at startup).
- Check storage health (disk-full, filesystem errors) that could have truncated writes.
- If in tests, ensure all encrypted blocks were fully flushed and the channel position is valid before decrypting.
Example fix
// before: reading a partially written segment tail ByteBuffer out = EncryptionUtils.decrypt(channel, null, true, reusableBuffers); // after: bound reads to the committed length long readableEnd = Math.min(channel.size(), committedSyncPosition); if (channel.position() + EncryptionUtils.ENCRYPTED_BLOCK_HEADER_SIZE > readableEnd) return null; // no more complete blocks ByteBuffer out = EncryptionUtils.decrypt(channel, null, true, reusableBuffers);
Defensive patterns
Strategy: validation
Validate before calling
long remaining = committedEnd - channel.position();
if (remaining < EncryptionUtils.ENCRYPTED_BLOCK_HEADER_SIZE)
return null; // truncated tail: no complete block left, stop reading instead of decrypting Try / catch
try {
ByteBuffer out = EncryptionUtils.decrypt(channel, null, true, reusableBuffers);
} catch (IllegalStateException e) {
if (e.getMessage().contains("metadata header")) {
// truncated/corrupt segment: mark segment end, stop replay
}
} Prevention
- Never truncate commit log segments externally
- Monitor disk-full conditions on the commitlog volume
- Only read encrypted segments up to their recorded sync position
When it happens
Trigger: decrypt() reading from a commit log segment that ends before a complete 16-byte header — truncated file, zero-padded tail, or reading past the last valid block.
Common situations: Disk-full during commit log write, abrupt crash leaving a partially written segment, or a file copied/truncated at the wrong size.
Related errors
- failed to decrypt commit log block
- Cannot safely construct descriptor for segment, as name…
- Cannot safely construct descriptor for segment, either from…
- commitlog_disk_access_mode =
- Encountered bad header at position
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/e1472652bde19e2c.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/security/EncryptionUtils.java:145
* Decrypt the input data, as well as manage sizing of the {@code outputBuffer}; if the buffer is not big enough,
* deallocate current, and allocate a large enough buffer.
*
* @return the byte buffer that was actaully written to; it may be the {@code outputBuffer} if it had enough capacity,
* or it may be a new, larger instance. Callers should capture the return buffer (if calling multiple times).
*/
public static ByteBuffer decrypt(ReadableByteChannel channel, ByteBuffer outputBuffer, boolean allowBufferResize, Cipher cipher) throws IOException
{
ByteBuffer metadataBuffer = reusableBuffers.get();
if (metadataBuffer.capacity() < ENCRYPTED_BLOCK_HEADER_SIZE)
{
metadataBuffer = ByteBufferUtil.ensureCapacity(metadataBuffer, ENCRYPTED_BLOCK_HEADER_SIZE, true);
reusableBuffers.set(metadataBuffer);
}
metadataBuffer.position(0).limit(ENCRYPTED_BLOCK_HEADER_SIZE);
channel.read(metadataBuffer);
if (metadataBuffer.remaining() < ENCRYPTED_BLOCK_HEADER_SIZE)
throw new IllegalStateException("could not read encrypted blocked metadata header");
int encryptedLength = metadataBuffer.getInt();
// this is the length of the compressed data
int plainTextLength = metadataBuffer.getInt();
outputBuffer = ByteBufferUtil.ensureCapacity(outputBuffer, Math.max(plainTextLength, encryptedLength), allowBufferResize);
outputBuffer.position(0).limit(encryptedLength);
channel.read(outputBuffer);
ByteBuffer dupe = outputBuffer.duplicate();
dupe.clear();
try
{
cipher.doFinal(outputBuffer, dupe);
}
catch (ShortBufferException | IllegalBlockSizeException | BadPaddingException e)
{
throw new IOException("failed to decrypt commit log block", e);View on GitHub (pinned to 88fd0f6a0e)