apache/cassandra · error · MarshalException

The data cannot be deserialized as a map

Error message

The data cannot be deserialized as a map

What it means

MapSerializer.deserialize reads the leading element count; if it is negative the bytes cannot possibly be a valid map, so this MarshalException is thrown explicitly. A negative count means the first 4 bytes were never a collection size — the data is either corrupt or produced by a different serializer.

Solutions

  1. Confirm the column's actual type matches the serializer used (check system_schema.tables).
  2. If the schema changed, migrate or rewrite old rows so values match the new type.
  3. Catch MarshalException and treat the row value as corrupt; use repair/scrub to fix stored data.
  4. Validate before deserialize (mapType.validate) to catch bad input early with clearer context.

Example fix

// before
Map<K,V> m = mapSerializer.deserialize(randomBytes, accessor);
// after
try {
    mapType.validate(randomBytes);
    Map<K,V> m = mapSerializer.deserialize(randomBytes, accessor);
} catch (MarshalException e) {
    throw new InvalidRequestException("Value is not a valid map: " + e.getMessage());
}
Defensive patterns

Strategy: validation

Validate before calling

// Java
if (bytes != null && bytes.remaining() >= 4) {
    int n = bytes.getInt(bytes.position());
    if (n < 0) throw new InvalidRequestException("first 4 bytes are not a map count");
}
mapType.validate(bytes);

Type guard

boolean looksLikeMapBytes(ByteBuffer b) { return b != null && b.remaining() >= 4 && b.getInt(b.position()) >= 0; }

Try / catch

try { return mapSerializer.deserialize(bytes, accessor); } catch (MarshalException e) { throw new InvalidRequestException("Cannot deserialize as map: " + e.getMessage()); }

Prevention

When it happens

Trigger: Deserializing a buffer whose first 4 bytes, read as a signed int, are negative — e.g. arbitrary bytes (text, blob) passed to a map serializer, or bit-flipped/corrupted stored cells.

Common situations: Schema mismatches where a column changed type but old data remains, reading through the wrong AbstractType, manual byte manipulation, disk corruption.

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/0a637c6c89155ff1. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/serializers/MapSerializer.java:130

            if (!accessor.isEmptyFromOffset(input, offset))
                throw new MarshalException("Unexpected extraneous bytes after map value");
        }
        catch (BufferUnderflowException | IndexOutOfBoundsException e)
        {
            throw new MarshalException("Not enough bytes to read a map");
        }
    }

    @Override
    public <I> Map<K, V> deserialize(I input, ValueAccessor<I> accessor)
    {
        try
        {
            int n = readCollectionSize(input, accessor);
            int offset = sizeOfCollectionSize();

            if (n < 0)
                throw new MarshalException("The data cannot be deserialized as a map");

            // If the received bytes are not corresponding to a map, n might be a huge number.
            // In such a case we do not want to initialize the map with that initialCapacity as it can result
            // in an OOM when put is called (see CASSANDRA-12618). On the other hand we do not want to have to resize
            // the map if we can avoid it, so we put a reasonable limit on the initialCapacity.
            Map<K, V> m = new LinkedHashMap<>(Math.min(n, 256));
            for (int i = 0; i < n; i++)
            {
                I key = readNonNullValue(input, accessor, offset);
                offset += sizeOfValue(key, accessor);
                keys.validate(key, accessor);

                I value = readNonNullValue(input, accessor, offset);
                offset += sizeOfValue(value, accessor);
                values.validate(value, accessor);

                m.put(keys.deserialize(key, accessor), values.deserialize(value, accessor));
            }

View on GitHub (pinned to 88fd0f6a0e)