apache/cassandra · error · MarshalException

Expected 0 or at least 4 bytes (%d)

Error message

Expected 0 or at least 4 bytes (%d)

What it means

DecimalSerializer serializes a BigDecimal as a 4-byte scale followed by an unscaled integer. A non-empty decimal value must therefore be at least 4 bytes; anything in between cannot even hold the scale field, so validate() rejects it with this formatted MarshalException including the actual byte size.

Source

Thrown at src/java/org/apache/cassandra/serializers/DecimalSerializer.java:62

        if (value == null)
            return ByteBufferUtil.EMPTY_BYTE_BUFFER;

        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;
    }

    public <T> void validate(T value, ValueAccessor<T> accessor) throws MarshalException
    {
        // We at least store the scale.
        if (!accessor.isEmpty(value) && accessor.size(value) < 4)
            throw new MarshalException(String.format("Expected 0 or at least 4 bytes (%d)", accessor.size(value)));
    }

    public String toString(BigDecimal value)
    {
        return value == null ? "" : value.toString();
    }

    public Class<BigDecimal> getType()
    {
        return BigDecimal.class;
    }
}

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Write decimal values via DecimalSerializer.serialize(BigDecimal) so the 4-byte scale is always present
  2. Check the column type matches the stored bytes (schema mismatch)
  3. If data is truncated on disk, restore from backup or repair
  4. Pad manual buffers to include the scale (4 bytes) plus unscaled value

Example fix

// before
ByteBuffer bad = ByteBufferUtil.bytes(new byte[]{1, 2}); // 2 bytes, no scale
// after
ByteBuffer ok = DecimalSerializer.instance.serialize(new BigDecimal("1.23")); // scale + unscaled
Defensive patterns

Strategy: validation

Validate before calling

public static void checkDecimal(ByteBuffer buf) {
    if (buf != null && buf.hasRemaining() && buf.remaining() < 4)
        throw new MarshalException("decimal value too short: " + buf.remaining());
}

Type guard

public static boolean isValidDecimalBytes(ByteBuffer buf) {
    return buf == null || !buf.hasRemaining() || buf.remaining() >= 4;
}

Try / catch

try {
    decimalType.validate(value);
} catch (MarshalException e) {
    logger.warn("Invalid decimal bytes: {}", e.getMessage());
}

Prevention

When it happens

Trigger: validate/deserialize called on a non-empty buffer of 1-3 bytes — e.g. a decimal column cell truncated, bytes of a different type stored in a decimal column, or a manual buffer written without the 4-byte scale.

Common situations: Schema drift (column type changed but old bytes remain); hand-crafted test data missing the scale integer; corruption from partial writes.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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