prestodb/presto · error · RedisProviderSerdeException
Error decoding key planHash which was of type String
Error message
Error decoding key planHash which was of type String
What it means
HistoricalStatisticsSerde.decodeKey can only decode a UTF-8 String key when the ByteBuffer is backed by a heap array (hasArray() == true). If the buffer is a direct/off-heap or otherwise non-array-backed ByteBuffer, it throws RedisProviderSerdeException because the UTF_8.decode path cannot be applied directly.
Source
Thrown at redis-hbo-provider/src/main/java/com/facebook/presto/statistic/HistoricalStatisticsSerde.java:46
import java.nio.charset.StandardCharsets;
/**
* Redis codec implementation for string keys and HistoricalPlanStatistics values.
*/
public class HistoricalStatisticsSerde
implements RedisCodec<String, HistoricalPlanStatistics>
{
private static final int ESTIMATED_BUFFER_SIZE_BYTES = 100 * 1024;
private final ThriftCodecManager thriftCodecManager = new ThriftCodecManager();
@Override
public String decodeKey(ByteBuffer bytes)
{
if (bytes.hasArray()) {
return StandardCharsets.UTF_8.decode(bytes).toString();
}
else {
throw new RedisProviderSerdeException("Error decoding key planHash which was of type String");
}
}
@Override
public ByteBuffer encodeKey(String key)
{
return ByteBuffer.wrap(key.getBytes(StandardCharsets.UTF_8));
}
@Override
public ByteBuffer encodeValue(HistoricalPlanStatistics historicalPlanStatistics)
{
ThriftCodec<HistoricalPlanStatistics> writeCodec = thriftCodecManager.getCodec(HistoricalPlanStatistics.class);
SliceOutput dynamicSliceOutput = new DynamicSliceOutput(ESTIMATED_BUFFER_SIZE_BYTES);
try {
ThriftProtocolUtils.write(historicalPlanStatistics, writeCodec, Protocol.BINARY, dynamicSliceOutput);
return ByteBuffer.wrap(dynamicSliceOutput.slice().getBytes());
}View on GitHub (pinned to 55bb57d202)
Solutions
- Add a fallback that copies direct buffer contents into a heap array before UTF-8 decoding: ByteBuffer.wrap(new byte[bytes.remaining()]).put(bytes.duplicate())
- Ensure the producer of the key ByteBuffer allocates on-heap (ByteBuffer.allocate / wrap)
- Update the Redis provider/serde integration so keys are passed as array-backed buffers
Example fix
// before
if (bytes.hasArray()) {
return StandardCharsets.UTF_8.decode(bytes).toString();
}
throw new RedisProviderSerdeException("Error decoding key planHash which was of type String");
// after
ByteBuffer dup = bytes.duplicate();
byte[] array = new byte[dup.remaining()];
dup.get(array);
return new String(array, StandardCharsets.UTF_8); Defensive patterns
Strategy: try-catch
Validate before calling
boolean isDecodable(ByteBuffer buf) {
return buf != null && buf.hasArray();
} Type guard
boolean isHeapByteBuffer(ByteBuffer buf) {
return buf != null && buf.hasArray();
} Try / catch
try {
key = serde.decodeKey(buffer);
} catch (RedisProviderSerdeException e) {
ByteBuffer dup = buffer.duplicate();
byte[] arr = new byte[dup.remaining()];
dup.get(arr);
key = new String(arr, StandardCharsets.UTF_8);
} Prevention
- Prefer on-heap ByteBuffers (allocate/wrap) when producing keys for this serde
- Add a hasArray() pre-check before calling decodeKey
- Keep a copy-based fallback in the caller for direct buffers
When it happens
Trigger: decodeKey is called with a direct ByteBuffer (allocated via allocateDirect) or a sliced/duplicated buffer with no accessible backing array, e.g. a key read from the Redis client into direct memory.
Common situations: Redis provider returns direct ByteBuffers for performance; a custom serde pipeline swaps heap buffers for direct ones; a library upgrade changes how ByteBuffers are produced upstream.
Related errors
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/9b16d55079b666b4.
Report an issue: GitHub.