apache/cassandra · error · org.apache.cassandra.transport.ProtocolException

Received frame with CUSTOM_PAYLOAD flag for native protocol

Error message

Received frame with CUSTOM_PAYLOAD flag for native protocol version < 4

What it means

ProtocolException thrown during message decoding when a frame carries the CUSTOM_PAYLOAD flag but the frame's native protocol version is older than V4, which introduced custom payloads. Older protocol versions cannot carry the trailing bytes-map that the flag promises, so the server rejects the frame as malformed. This is a strict version/feature compatibility check.

Source

Thrown at src/java/org/apache/cassandra/transport/Message.java:457

            throw ErrorMessage.wrap(e, streamId);
        }
    }

    abstract static class Decoder<M extends Message>
    {
        static Message decodeMessage(Channel channel, Envelope inbound)
        {
            boolean isRequest = inbound.header.type.direction == Direction.REQUEST;
            boolean isTracing = inbound.header.hasFlag(Flag.TRACING);
            boolean isCustomPayload = inbound.header.hasFlag(Flag.CUSTOM_PAYLOAD);
            boolean hasWarning = inbound.header.hasFlag(Flag.WARNING);

            TimeUUID tracingId = isRequest || !isTracing ? null : CBUtil.readTimeUUID(inbound.body);
            List<String> warnings = isRequest || !hasWarning ? null : CBUtil.readStringList(inbound.body);
            Map<String, ByteBuffer> customPayload = !isCustomPayload ? null : CBUtil.readBytesMap(inbound.body);

            if (isCustomPayload && inbound.header.version.isSmallerThan(ProtocolVersion.V4))
                throw new ProtocolException("Received frame with CUSTOM_PAYLOAD flag for native protocol version < 4");

            Message message = inbound.header.type.codec.decode(inbound.body, inbound.header.version);
            message.setSource(inbound);
            message.setCustomPayload(customPayload);

            if (isRequest)
            {
                assert message instanceof Request;
                Request req = (Request) message;
                Connection connection = channel.attr(Connection.attributeKey).get();
                req.attach(connection);
                if (isTracing)
                    req.setTracingRequested();
            }
            else
            {
                assert message instanceof Response;
                if (isTracing)

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Raise the client's configured protocol version to V4 or higher (e.g. set protocolVersion to v4/v5 in the driver config).
  2. Update the driver to a modern version that only sets CUSTOM_PAYLOAD on V4+ connections.
  3. If the payload is not needed, remove the custom-payload option from the client configuration.
  4. Audit for mixed-version client libraries that hardcode flags independent of negotiated version.

Example fix

// before: client pinned to old protocol with custom payload enabled
Cluster.builder().addContactPoint(host).withProtocolVersion(ProtocolVersion.V3).build();
// after: allow V4+ so CUSTOM_PAYLOAD is valid
Cluster.builder().addContactPoint(host).withProtocolVersion(ProtocolVersion.V4).build();
Defensive patterns

Strategy: validation

Validate before calling

// check version/feature compatibility before enabling custom payloads
if (chosenProtocolVersion < ProtocolVersion.V4) disableCustomPayload();

Try / catch

try { connect(); } catch (ProtocolException e) { if (e.getMessage().contains("CUSTOM_PAYLOAD")) { upgradeProtocolVersionOrDisablePayloads(); } }

Prevention

When it happens

Trigger: A client (or driver) claims protocol v3 or lower in the frame header but sets the CUSTOM_PAYLOAD flag (0x04) in the frame's flags byte; decodeMessage in Message.Decoder detects version < V4 together with the flag.

Common situations: Driver misconfiguration pinning an old protocol version (e.g. protocolVersion: v3) while using a newer feature such as custom payloads for request tracing tags; mixing driver components of different versions; hand-rolled clients setting flags unconditionally.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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