apache/cassandra · error · InvalidTypeException

Invalid decimal value, expecting at least 4 bytes but got

Error message

Invalid decimal value, expecting at least 4 bytes but got 

What it means

CQL decimal wire format is a 4-byte big-endian scale (varint exponent) followed by the unscaled BigInteger bytes. deserialize() requires at least 4 bytes to read the scale; fewer means the payload cannot be a decimal and the codec throws InvalidTypeException.

Source

Thrown at src/java/org/apache/cassandra/cql3/functions/types/TypeCodec.java:1342

        {
            if (value == null) return null;
            BigInteger bi = value.unscaledValue();
            int scale = value.scale();
            byte[] bibytes = bi.toByteArray();

            ByteBuffer bytes = ByteBuffer.allocate(4 + bibytes.length);
            bytes.putInt(scale);
            bytes.put(bibytes);
            bytes.rewind();
            return bytes;
        }

        @Override
        public BigDecimal deserialize(ByteBuffer bytes, ProtocolVersion protocolVersion)
        {
            if (bytes == null || bytes.remaining() == 0) return null;
            if (bytes.remaining() < 4)
                throw new InvalidTypeException(
                "Invalid decimal value, expecting at least 4 bytes but got " + bytes.remaining());

            bytes = bytes.duplicate();
            int scale = bytes.getInt();
            byte[] bibytes = new byte[bytes.remaining()];
            bytes.get(bibytes);

            BigInteger bi = new BigInteger(bibytes);
            return new BigDecimal(bi, scale);
        }
    }

    /**
     * This codec maps a CQL {@link DataType#cdouble()} to a Java {@link Double}.
     */
    private static class DoubleCodec extends PrimitiveDoubleCodec
    {

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Encode decimals properly: scale int followed by unscaled BigInteger.toByteArray() (see TypeCodec decimal serialize).
  2. Match the codec to the column's actual type (double codec for 8-byte doubles, etc.).
  3. Ensure the ByteBuffer position is at the value start (duplicate/rewind) so remaining() reflects the full value.
  4. Replace truncated/partial payloads at the source — the data itself is corrupt if genuinely < 4 bytes.

Example fix

// before
ByteBuffer b = ByteBuffer.allocate(2).putShort((short)3); // truncated
decimalCodec.deserialize(b, version); // throws
// after
byte[] unscaled = new BigInteger("12345").toByteArray();
ByteBuffer b = ByteBuffer.allocate(4 + unscaled.length).putInt(2).put(unscaled);
decimalCodec.deserialize(b, version);
Defensive patterns

Strategy: validation

Validate before calling

if (bytes == null || bytes.remaining() < 4)
    throw new IllegalArgumentException("decimal needs >= 4 bytes, got " + (bytes == null ? 0 : bytes.remaining()));

Type guard

boolean isDecimalBuffer(ByteBuffer b) {
    return b != null && b.remaining() >= 4;
}

Try / catch

try {
    return decimalCodec.deserialize(bytes, protocolVersion);
} catch (InvalidTypeException e) {
    throw new IllegalStateException("Corrupt or wrong-typed decimal payload: " + bytes.remaining() + " bytes", e);
}

Prevention

When it happens

Trigger: Calling decimalCodec.deserialize(bytes, protocolVersion) where 0 < bytes.remaining() < 4 — e.g. decoding an int (4 bytes is OK, but 2-byte smallint isn't), float, or truncated buffer through the decimal codec.

Common situations: Schema changed from decimal to float/double/int while codecs assume decimal; truncated network or SSTable bytes; custom serialization writing only the unscaled bytes without the 4-byte scale prefix; misaligned buffer positions.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/8809f23b3af0bb97. Report an issue: GitHub.