Skip to content

Fix NULL key/value when consuming legacy MessageSets with flexible Fetch versions (v12+) - #5550

Open
Thomas (delthas) wants to merge 3 commits into
confluentinc:masterfrom
delthas:fix/legacy-msgset-flexver-fetch
Open

Fix NULL key/value when consuming legacy MessageSets with flexible Fetch versions (v12+)#5550
Thomas (delthas) wants to merge 3 commits into
confluentinc:masterfrom
delthas:fix/legacy-msgset-flexver-fetch

Conversation

@delthas

Copy link
Copy Markdown

Fixes #5549

Symptom

When consuming from a cluster or topic using the legacy message format (log.message.format.version < 0.11, MsgVersion v0..v1), fetches succeed and offsets advance, but every message is delivered to the application with NULL key and NULL value (offsets and timestamps are correct), together with consumer errors such as:

Unsupported Message(Set) MagicByte 97 at offset 1525458452426752

Happening since v2.5.0. On v2.5.x fetches fail/stall instead; from v2.6.0 onwards messages are delivered with NULL key/value as described.

Found in production through node-rdkafka 3.6.1 (librdkafka 2.12.0), consuming from Apache Kafka 3.9.1 brokers that pin log.message.format.version=0.10.2.2 for rolling-upgrade compatibility.

Root cause

Since the Fetch RPC was upgraded to flexible versions (#4584, v2.5.0), the FetchResponse buffer is flagged as a flexible-version response. The legacy MessageSet reader (rd_kafka_msgset_reader_msg_v0_1()) reads the Message Key and Value with rd_kafka_buf_read_kbytes(), which switches to compact (varint-prefixed) parsing when that flag is set — but legacy MessageSets always use fixed-width (int32-prefixed) encodings regardless of the Fetch version. The first 0x00 byte of the length prefix parses as a compact NULL, and the reader then drifts into the middle of the message bytes, misparsing the rest of the MessageSet.

Fix

Add rd_kafka_buf_read_kbytes_fixed(), which always reads the fixed-width encoding, and use it for the legacy Message Key and Value.

Testing

  • The mock broker previously rejected any MessageSet with MagicByte != 2, so this path was untestable: a second commit adds support for legacy uncompressed v0..v1 MessageSets to the mock broker.
  • New regression test 0187 caps the ProduceRequest version on the mock cluster so the producer writes MsgVersion v1 (or v0) MessageSets, and verifies a consumer using a flexible Fetch version receives them intact. Fails without the fix, passes with it.
  • Also reproduced and verified against a real Apache Kafka 3.9 broker configured with inter.broker.protocol.version=0.10.2.2 / log.message.format.version=0.10.2.2.

@confluent-cla-assistant

confluent-cla-assistant Bot commented Jul 15, 2026

Copy link
Copy Markdown

🎉 All Contributor License Agreements have been signed. Ready to merge.
✅ delthas
Please push an empty commit if you would like to re-run the checks to verify CLA status for all contributors.

@delthas
Thomas (delthas) force-pushed the fix/legacy-msgset-flexver-fetch branch 2 times, most recently from 79f8b8f to 55a8d8d Compare July 15, 2026 12:47
@delthas
Thomas (delthas) marked this pull request as ready for review July 15, 2026 12:48
@delthas
Thomas (delthas) requested a review from a team as a code owner July 15, 2026 12:48
@Ankith-Confluent

Copy link
Copy Markdown
Member

/sem-approve

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR Thomas (@delthas)!
The PR looks good. Just one minor comment from my side.

Comment thread CHANGELOG.md Outdated
@Ankith-Confluent

Copy link
Copy Markdown
Member

/sem-approve

@Ankith-Confluent

Copy link
Copy Markdown
Member

Hi Thomas (@delthas)
Could you please rebase your PR with master.

…tch versions

Since the Fetch RPC was upgraded to flexible versions (v12+, KIP-951
prework, v2.5.0), messages stored in the legacy MessageSet format
(MsgVersion v0..v1, brokers or topics with
log.message.format.version < 0.11) were returned to the application
with NULL key and value.

The legacy MessageSet reader used rd_kafka_buf_read_kbytes() to read
the Message Key and Value, which parses a compact (varint-prefixed)
length when the buffer is flagged as a flexible-version response.
Legacy MessageSets always use fixed-width (int32-prefixed) encodings
regardless of the FetchResponse version, so the first 0x00 length byte
was read as a varint compact NULL, and the reader then drifted into
the middle of the message, misparsing the rest of the MessageSet with
errors such as 'Unsupported Message(Set) MagicByte'.

Add rd_kafka_buf_read_kbytes_fixed() which always reads the
fixed-width encoding, and use it for the legacy Message Key and Value.
The mock broker rejected any produced MessageSet with a MagicByte
other than 2 with UNSUPPORTED_VERSION, making it impossible to test
consuming from clusters with log.message.format.version < 0.11.

Accept uncompressed legacy v0..v1 MessageSets: validate the MessageSet
layout, count its messages, and assign an absolute log offset to each
message in the set. Compressed legacy MessageSets are still rejected
with UNSUPPORTED_VERSION.
Regression test for consuming messages stored in the legacy
MessageSet format (MsgVersion v0..v1) with flexible Fetch versions
(v12+): cap the ProduceRequest version on the mock cluster so the
producer writes MsgVersion v1 (Produce <= v2) or v0 (Produce <= v1)
MessageSets, then verify that a consumer fetching with a flexible
Fetch version receives all messages with intact key and value.
@delthas
Thomas (delthas) force-pushed the fix/legacy-msgset-flexver-fetch branch from 9e03b4e to e7ff1d0 Compare July 28, 2026 14:06
@delthas

Copy link
Copy Markdown
Author

Hi Ankith L (@Ankith-Confluent),

Done.

@Ankith-Confluent

Copy link
Copy Markdown
Member

/sem-approve

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have approved the PR Thomas (@delthas)
LGTM!

We will merge it after another round of review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consumer returns NULL key/value for legacy MessageSet format (message.format.version < 0.11) since v2.5.0

2 participants