Request and Response Protocol Versions in Apache Kafka

Request and Response Protocol Versions in Apache Kafka

How old and new clients and brokers understand each other: the wire format, API keys, per-API version numbers, the ApiVersions handshake, flexible encodings, feature flags and the rules that make rolling upgrades possible.

Imagine an international airport where pilots from every country talk to the control tower. Some pilots trained decades ago and use an older phrasebook. Others arrived last week with a brand-new edition. The tower cannot shut down for a day to teach everyone the new edition, and it cannot refuse the old pilots either. So before any serious conversation begins, each pilot and the tower quickly agree: “Which edition of the phrasebook shall we both use today?”

Apache Kafka lives in exactly this situation. Thousands of applications, written in many languages and built in many years, talk to brokers that are upgraded on a different schedule. The mechanism that keeps this crowded conversation orderly is the request and response protocol versioning system. This tutorial explains it from the first principles to the advanced details that architects need when planning upgrades and debugging strange compatibility errors.

1Why Kafka Needs Protocol Versions

Before looking at any bytes, we must understand the real-world problem that versioning solves.

A distributed system never upgrades all at once

A Kafka cluster may hold dozens of brokers. Around it may sit hundreds of producers, consumers, stream processors, connectors, monitoring agents and administrative tools. These pieces are owned by different teams, written in different programming languages, and deployed on different days. It is almost never possible to stop everything and upgrade everything at the same moment.

That means, at any given time, a cluster is likely to be talking to software of several ages. A broker from this year may be serving a client library from three years ago and a command-line tool from last month. Everyone still expects messages to flow.

Simple Analogy

Think of a train network. New trains with modern doors arrive constantly, but old platforms remain in use. The railway cannot close every station to rebuild all platforms overnight. Instead, trains and platforms follow agreed standards so that old and new can still work together while upgrades happen gradually.

What could go wrong without versioning

Suppose the Kafka team wants to add a new field to the request that fetches messages, for example a field telling the broker which transaction isolation level the consumer wants. If the client and broker simply assumed the newest format, an old client would send a request without the field, and a new broker would misread the following bytes as if they belonged to that field. The result would be garbage, crashes or silent corruption.

Going the other way is just as dangerous. A new client sending an extra field to an old broker would confuse it, because the old broker has no idea what the field means or how long it is.

Problem 1

Layout drift

Adding, removing or reordering fields changes the meaning of every byte that follows unless both sides know exactly which layout is in use.

Problem 2

Behavior drift

Even with an identical layout, the meaning of a field can change between releases, so both sides must agree on the behavior too.

Problem 3

Unknown capabilities

A client does not know in advance what the broker it just connected to can do. Guessing wrong causes errors.

Problem 4

Upgrade coordination

Forcing every application and broker to change together would make Kafka unusable in real organizations.

The solution in one sentence

Every request type in Kafka carries an explicit version number, every broker advertises the range of versions it understands for each request type, and every client picks a version that both sides support before doing real work. That simple agreement, repeated for every request type, is the heart of Kafka’s compatibility story.

“Versioning turns a dangerous guess into a polite negotiation.”
i
Keep two ideas apart

Protocol versions describe how requests and responses are shaped on the network. Message format versions describe how records are stored inside a batch. They are related, but they are separate ideas, and we will study both.

2The Kafka Wire Protocol in Plain Words

Kafka does not use a web-style protocol. It has its own compact, binary request and response conversation over ordinary network connections.

A custom binary protocol over TCP

Kafka clients open a normal network connection to a broker, typically using the TCP transport, optionally wrapped in TLS encryption. Over this connection they exchange messages in a purpose-built binary format. It is not text, it is not JSON, and it is not HTTP. Binary formats are much smaller and much faster to parse than text formats, which matters when a broker handles hundreds of thousands of requests each second.

Simple Analogy

Compare a hand-written letter with a pre-printed form that has tiny numbered boxes. The letter is friendly to read but slow to process. The form is unreadable to a stranger but a clerk can process a thousand of them in an hour because every box always means the same thing.

The request and response rhythm

The conversation follows a strict pattern. The client sends a request. The broker sends back exactly one response for that request. There is no unsolicited chatter from the broker. Requests on one connection are answered in the order they were received, which keeps the protocol simple and predictable.

A client may send several requests without waiting for earlier answers. This is called pipelining. It improves throughput because the network does not sit idle while waiting for each individual answer.

Size-prefixed framing

Every request and every response begins with a number that says how many bytes follow. This is like putting a note on the front of each parcel that says “this parcel weighs 300 grams”. The receiver reads the number, then reads exactly that many bytes, and knows the parcel is complete. Without this, a reader would not know where one message ends and the next begins on a continuous stream of bytes.

1

Size

A four-byte number announcing the length of the rest of the request.

2

Header

A small block that says which kind of request this is, which version of it this is, and a number used to match the eventual response.

3

Body

The actual content, whose exact shape depends on the request type and the version named in the header.

Why the version sits in the header

The receiver must know how to read the body before reading it. Since the body’s layout depends on the version, the version is placed in the header, which itself has a small, stable shape. The receiver reads the header first, looks up the request type and version, chooses the matching layout, and then parses the body accordingly.

!
Common misconception

Kafka does not have one protocol version for the entire connection. It has a separate version number for each kind of request. A single connection may carry a produce request at one version and a fetch request at a very different version.

1
response per request
Many
request types, each with own versions
0
text parsing on the hot path

3API Keys: The Menu of Requests

Every kind of request in Kafka has a small numeric identifier called an API key.

Think of a restaurant where each dish on the menu has a number. Instead of saying “one grilled chicken with rice and vegetables”, a customer can say “number twelve”. The kitchen knows exactly what that means. Kafka does the same: the first meaningful item in a request header is the API key, a small number that tells the broker what the client wants.

API

Produce

Sends batches of messages from a producer to the leader of a partition so they can be appended to the log.

API

Fetch

Reads messages from a partition. Consumers use it, and so do follower replicas copying data from leaders.

API

Metadata

Asks which topics and partitions exist and which broker leads each one. Clients use this to route requests correctly.

API

ApiVersions

Asks a broker which request types and which version ranges it supports. This is the negotiation request.

API

ListOffsets

Finds offsets by time or by position such as earliest or latest, used when starting or resetting consumers.

API

Group coordination requests

Join group, sync group, heartbeat and leave group requests that let consumers in a group share partitions.

API

Offset commit and fetch

Store and retrieve how far a consumer group has read in each partition.

API

Administrative requests

Create topics, delete topics, alter configurations, manage access rules and describe the cluster.

API

Transaction requests

Initialize producer identity, add partitions to a transaction and commit or abort it.

API

Authentication requests

Handshake and authenticate requests used by SASL security mechanisms.

Internal APIs

Not all requests come from clients. Brokers and controllers also talk to each other with their own requests. Replica followers use fetch requests to copy data. Controllers exchange requests to elect leaders, register brokers and share metadata. Some of these APIs are marked for internal use only, and their availability to ordinary clients may be restricted. They still use the same version mechanism.

API keys never change meaning

Once a number is assigned to a request type, it belongs to that type forever. If Kafka removes a request type, its number is retired and not reused for something else. This stability is important, because an old client using API key one must always mean the same request as it did years ago, even if the broker has a completely different set of newer request types.

Simple Analogy

Emergency telephone numbers stay the same for decades because everyone depends on them. Reassigning a well-known number to a different service would cause chaos. Kafka treats API key numbers the same way.

i
Growth over time

The list of API keys has grown steadily over the years as Kafka gained transactions, quotas, security, administration features and controller-quorum requests. Each new feature tends to arrive as a new request type or as a new version of an existing one.

4Request and Response Headers

The header is the small label on every message. It, too, has versions.

What the request header carries

Field 1

API key

Which kind of request this is.

Field 2

API version

Which shape of that request the body follows.

Field 3

Correlation identifier

A number chosen by the client and echoed back in the response, so pipelined answers can be matched to their questions.

Field 4

Client identifier

A human-readable name for the application, useful for quotas, logging and metrics.

Simple Analogy

A courier label shows what type of parcel this is, which handling rules apply, a tracking number that will appear on the delivery receipt, and the sender’s name. The correlation identifier in Kafka is that tracking number.

Header versions

The header format itself has evolved. The earliest header held only the four fields above. A later header version, introduced together with the flexible encoding described in a later chapter, added a section at the end for optional tagged fields. So there are several header versions, and the header version used for a request is decided by the request’s own version.

HeaderContentsUsed when
Request header, older styleAPI key, version, correlation identifier, client identifierOlder, non-flexible request versions
Request header, flexible styleSame as above plus optional tagged fieldsNewer, flexible request versions
Response header, older styleCorrelation identifier onlyOlder, non-flexible response versions
Response header, flexible styleCorrelation identifier plus optional tagged fieldsNewer, flexible response versions

The special case of ApiVersions

There is one deliberate exception. The response to the ApiVersions request always uses the oldest, simplest response header, even when the request itself uses a newer version. The reason is practical: the ApiVersions exchange is the very first conversation, so the client cannot yet know what the broker supports. Using the simplest header for this one response means even a very old client can read at least the essential part of the answer.

!
Mistake seen in custom clients

People writing their own client libraries sometimes apply the newer response header to the ApiVersions response and then fail to read it. Remember this special rule whenever you implement or debug a protocol client.

5Every API Has Its Own Version Line

Versions in Kafka are not global. Each request type keeps its own counter.

Independent counters

Imagine a large company where every department publishes its own policy handbook. The finance handbook may be on edition nine, the travel handbook on edition three, and the security handbook on edition twelve. There is no single edition number for the whole company, because each department changes its rules at its own pace.

Kafka works the same way. The fetch request might be at a high version because it has been refined many times, while a rarely used administrative request might still be on its first or second version. Each version number is meaningful only together with its API key.

Simple Analogy

Software libraries, apps on a phone and operating systems all have separate version numbers. Saying “version 5” means nothing without saying “version 5 of what”. A Kafka version number is always “version N of API key K”.

Why not one number for everything

Advantages of per-API versions

  • A change to one request does not force a version bump for unrelated requests
  • Clients only need to upgrade the specific behaviors they actually use
  • Broker developers can evolve one feature without touching others
  • Compatibility can be reasoned about one request at a time

Costs of per-API versions

  • The total number of combinations is large and must be tracked carefully
  • Documentation and testing must cover many separate version ranges
  • Client libraries must implement version selection for each request type
  • Diagnosing problems requires knowing which request and which version were involved

What a version range means

For each API key, a broker supports a contiguous range of versions, from a minimum to a maximum. For example, a broker might support versions three through nine of one request. A client that only understands versions zero through two cannot use that request with this broker. A client that understands versions five through twelve can use versions five through nine.

Broker supportsClient supportsOverlapResult
3 to 90 to 2NoneClient cannot use this request against this broker
3 to 95 to 125 to 9Use version 9, the highest shared
0 to 50 to 120 to 5Use version 5, the highest shared
0 to 120 to 120 to 12Use version 12
i
Ranges are per direction of thought

A version range is a promise about what a party can read and write for one API key. Both the client and the broker publish or embed their own range, and the negotiation is simply finding the overlap.

6The ApiVersions Handshake

The very first request on a fresh connection is usually a polite question: what can you do?

The conversation step by step

1

Connect

The client opens a network connection to a broker, applying encryption if configured.

2

Ask for capabilities

The client sends an ApiVersions request, usually at the newest version it knows.

3

Receive the menu

The broker replies with a list: for every API key it supports, the minimum and maximum version it will accept.

4

Compute the overlap

For every API key the client cares about, it finds the highest version supported by both sides.

5

Proceed with real work

All later requests on that connection use the negotiated versions. Security handshakes and data requests follow.

sequenceDiagram
    participant C as Client
    participant B as Broker
    C->>B: Open connection
    C->>B: ApiVersions request
    B-->>C: List of API keys with min and max versions
    C->>C: Choose highest shared version per API key
    C->>B: Metadata request at chosen version
    B-->>C: Metadata response
    C->>B: Produce request at chosen version
    B-->>C: Produce response
        
FIG 1 — A fresh connection negotiates versions before real work
Simple Analogy

Two people meet at a conference. Before discussing technical details, they ask, “Which language are you most comfortable in?” They discover they both speak English fluently and one also speaks French. They choose English for the deep conversation because it is the best language they share.

What the response contains

The ApiVersions response is essentially a table. Each row says, for one API key, the lowest and highest version this broker supports. In newer versions of the response, the table also includes information about broker features, such as the cluster-wide metadata version and other finalized feature levels. We cover those later.

Software name and version information

A later version of the ApiVersions request lets the client tell the broker the name and version of its client software, for example “the standard Java client, release such-and-such”. Brokers can expose this in metrics and logs. This is very valuable in real operations, because it lets administrators ask “which applications are still using an ancient client library?” and plan upgrades with evidence.

Finding outdated clients

Before removing support for old protocol versions, an operations team can look at broker metrics that group connections by client software name and version, then contact the owners of anything too old.

Debugging odd behavior

If one application behaves differently from the rest, knowing its client library version often explains why.

!
The client does the choosing

The broker advertises what it can accept. It is the client library that computes the overlap and decides which version to send. If the client’s decision is wrong, the broker will reject the request.

7What If the Client Is Newer Than the Broker

The negotiation has a clever trick for the case where even the ApiVersions request itself is too new for the broker.

The awkward first message

A client always begins with ApiVersions. But suppose the client is very modern and sends the newest version of ApiVersions to a very old broker that has never heard of that version. The old broker cannot parse the request. If it simply dropped the connection, the client would have no clue why.

The graceful fallback

Kafka handles this with a special rule. When a broker receives an ApiVersions request whose version it does not support, it replies using the oldest response format, with an error code meaning “unsupported version”, and it still includes its table of supported versions in that reply. The client reads the error, looks at the table, sees the highest ApiVersions version the broker does support, and simply asks again using that older version. This is why the ApiVersions response always uses the oldest header: even the most ancient broker can produce it.

sequenceDiagram
    participant C as New client
    participant B as Old broker
    C->>B: ApiVersions at newest version
    B-->>C: Error unsupported version, plus supported table in oldest format
    C->>C: Note broker maximum for ApiVersions
    C->>B: ApiVersions again at older version
    B-->>C: Full list of supported API versions
    C->>B: Normal requests at negotiated versions
        
FIG 2 — The fallback path when ApiVersions itself is too new
Simple Analogy

A traveler greets a shopkeeper in a modern slang the shopkeeper does not understand. The shopkeeper says, “Sorry, I only know the old greeting,” and helpfully speaks it slowly. The traveler nods, repeats the greeting in the old style, and the conversation continues smoothly.

Older brokers with no ApiVersions at all

Very early Kafka brokers, from before the negotiation mechanism was invented, do not understand the ApiVersions request at all. Modern clients that want to support such ancient brokers traditionally need a fallback: either a manual setting telling the client which broker release to assume, or a guess based on trial and error. Some client libraries offered exactly such a “broker version fallback” setting for this reason. Today this situation is rare, and newer Kafka releases have deliberately dropped support for the oldest protocol versions, as we discuss later.

What happens to unsupported requests in general

If a client sends some other request at a version the broker does not support, the broker will typically return an error indicating the version is unsupported. Depending on the type of request and the broker’s design, it may also close the connection. Well-written clients avoid this entirely because they only choose versions from the negotiated overlap.

i
Recognize the error

If your application logs mention an unsupported version error, the first suspicion is a mismatch between the client library and the broker, or a custom client that skipped negotiation. Compare the versions each side supports for the failing request.

8Flexible Versions and Tagged Fields

A major redesign of the encoding made future changes cheaper and safer.

The old problem: every change needs a new version

In the classic encoding, adding even a small optional field meant creating a whole new version of the request, and every client and broker had to know about it. The version numbers rose quickly, and old brokers that did not know a new field could not tolerate it at all.

The idea: optional extras that can be skipped

A Kafka improvement proposal introduced flexible versions. In a flexible version, every message ends with a section of tagged fields. A tagged field has a numeric tag, a size and its data. A reader that recognizes the tag reads it. A reader that does not recognize the tag knows exactly how many bytes to skip, so it can safely ignore the field and continue with everything else. This turns many changes into harmless, ignorable additions.

Simple Analogy

Picture a parcel with an extra pocket on the outside for optional notes. Each note carries a label and a length. A receiver who understands a label reads the note. A receiver who does not simply leaves that note in the pocket. The main contents of the parcel stay intact either way.

Compact encodings

Flexible versions also switched to more compact ways of writing lengths. Instead of writing every string and array length in a fixed number of bytes, they use variable-length integers, which take a single byte for small values. This saves space when there are many small strings and arrays, which is very common in Kafka requests that list many topics and partitions.

AspectClassic versionsFlexible versions
String and array lengthsFixed-size numbersCompact variable-length numbers
Optional additionsRequire a new versionCan often ride as tagged fields
Message endingBody ends with the last fieldBody ends with a tagged fields section
HeaderBasic headerHeader with tagged fields
Size on the wireLarger for listsSmaller for lists

Where tagged fields help most

Optional diagnostics

Extra information that helps monitoring but is not essential for correctness can be added without disturbing older readers.

Rarely used features

A field needed by only a few callers does not need to bloat every request for everyone else.

Experiments and gradual rollouts

New information can be introduced carefully and, if unused, is ignored by receivers that have not adopted it.

!
Not a free pass

Tagged fields are for information that a receiver can safely ignore. If a change alters the meaning of existing fields, or if the receiver must understand the new field to behave correctly, a new version of the API is still required.

9Message Format Versions Inside Record Batches

The records inside a produce or fetch request have their own format version, separate from the request version.

Two layers of versioning

Picture a shipping container filled with boxes. The container has a standard shape so that cranes and trucks can handle it. The boxes inside have their own packing layout. Kafka has this same double layer. The request is the container, and the records are the boxes. The request version says how the container is laid out. The message format version says how the boxes inside are packed.

The historical generations

0

Format zero

The original design. Each message has a key, a value and basic integrity check information, but no timestamp.

1

Format one

Added timestamps to messages, enabling time-based retention and time-based lookups.

2

Format two

Introduced record batches as the main unit, headers on records, compact encoding of fields inside a batch, and the metadata needed for idempotent and transactional producers.

The number that identifies this generation is stored in a small field within each batch, historically called the magic byte. A reader looks at it first and then knows exactly how to interpret the rest.

Down-conversion for older consumers

If an old consumer asks for messages using an old version of the fetch request that only understands an older message format, the broker may need to convert stored messages to that older layout on the fly. This is called down-conversion. It costs CPU and memory, and it prevents the broker from using its most efficient path for sending data straight from disk to network, called zero-copy. A cluster full of old consumers can therefore be noticeably slower than one full of modern ones.

Simple Analogy

A restaurant that cooks in bulk can serve modern guests quickly. If some guests demand an old-fashioned recipe, the kitchen has to prepare each of their plates separately, which is much slower and uses more staff. The old guests are fully served, but the kitchen pays for it.

LayerWhat it describesWhere the number appearsEvolves when
Request versionLayout of the request and response bodiesRequest headerFields of an API change
Message format versionLayout of records in a batchInside each record batchStorage layout of records changes
Feature or metadata versionWhat the cluster as a whole is allowed to doCluster metadataOperators finalize a new level
i
Modern releases simplify this

Recent Kafka releases removed support for the two oldest message formats and their related broker settings, which greatly reduces down-conversion and removes a class of upgrade mistakes. Always check the release notes of your target version for the exact cut-off.

10How a New Version Is Born

Changing the protocol in Kafka is a deliberate, public and carefully reviewed process.

The improvement proposal process

Kafka changes that affect the protocol, public interfaces or compatibility are proposed in a written document called a Kafka Improvement Proposal, usually shortened to KIP. The document describes the problem, the proposed new fields or requests, how old and new versions will coexist, and how upgrade and downgrade will behave. The community reviews it, discusses it openly and votes before the change is accepted.

1

Write the proposal

A contributor documents the need, the new request or new fields, and the compatibility plan.

2

Discuss and vote

The community reviews the design, raises concerns about compatibility and finally votes.

3

Describe the schema

The request and response are described in a schema file that lists each field and the range of versions in which it exists.

4

Generate the code

Build tooling reads the schemas and generates the reading and writing code for every version, so that hand-written parsing bugs are avoided.

5

Implement behavior

Broker and client code is updated to produce and handle the new version, while continuing to serve all older supported versions.

6

Release and document

The new version appears in a release, and the protocol documentation and upgrade notes are updated.

Schemas as the single source of truth

Each request and response type is defined in a small structured schema. For every field, the schema states the versions in which the field exists, whether it may be empty in certain versions, and whether it is an optional tagged field. The client and broker code that reads and writes the bytes is generated from these schemas. This keeps every version consistent and lets developers see, at a glance, the entire history of an API.

Simple Analogy

A building’s blueprint is the single source of truth. Plumbers, electricians and carpenters all work from the same drawings, so their work fits together. If each trade improvised from memory, walls and pipes would collide.

i
Reading history

Because fields are annotated with the versions in which they appear, the schema for any API is like a timeline. You can see when a field was added, when it became optional and when it was removed.

11Compatibility Rules: What May Change and What May Not

Freedom to evolve depends on a few strict rules that Kafka developers follow.

Backward and forward compatibility

Backward compatibility means new software can still work with old partners. Forward compatibility means old software can tolerate the presence of newer partners. Kafka aims for both at the level of clients and brokers within supported ranges: a new client should be able to talk to a reasonably old broker, and an old client should be able to talk to a newer broker, as long as the versions they share still exist.

Simple Analogy

A modern power socket that still accepts old plugs is backward compatible. A modern plug that still fits in older sockets is forward compatible. The most comfortable travel adapters manage both.

The rules of the road

Rule 1

Never change an existing version

Once a version has been released, its layout and behavior are frozen. Any change, however small, becomes a new version.

Rule 2

Keep old versions working

Brokers continue to understand older versions of each API for a long time, so older clients keep working.

Rule 3

Add fields only in new versions

New fields exist only from the version that introduces them. Older versions of the request never carry them.

Rule 4

Provide sensible defaults

When an old client sends a request lacking a newer field, the broker must behave as if the field held a sensible default that matches the old behavior.

Rule 5

Deprecate slowly, remove deliberately

Removal of old versions is announced in advance, tied to a major release and explained in the upgrade notes.

Rule 6

Document every change

Every change is described in the proposal, the release notes and the protocol documentation.

Examples of changes and their consequences

Kind of changeNeeds a new version?Why
Add an ignorable optional detail in a flexible versionOften no, may use a tagged fieldReaders that do not know it can skip it
Add a required field whose absence changes behaviorYesOld readers would misparse or misbehave
Change the meaning of an existing fieldYesSame bytes would mean something different
Remove a fieldYesLayout shifts for everything after it
Add a new error codeDepends on how clients reactOld clients may not understand a new code
Introduce a new request typeNew API key at its first versionOld brokers will simply not list it
!
Hidden behavior changes

Even when the layout stays the same, the broker’s behavior can differ for different versions of a request. A client that chooses an older version may see older semantics, such as a different error code or a simpler response, while a newer version may unlock richer behavior.

12Bidirectional Client Compatibility

The negotiation mechanism opened the door to a very friendly policy: new clients and old brokers can work together, and so can old clients and new brokers.

The historical turning point

In the early days of Kafka, a client library could only talk to brokers of the same age or newer. Upgrading a client required upgrading the brokers first. Once the ApiVersions request was introduced and client libraries learned to use it, a new client could look at what an older broker supported and speak accordingly. Newer clients could now talk to a broad range of older brokers, and older clients continued to talk to newer brokers, as newer brokers kept the older versions available.

flowchart LR
    OC["Older client library"] -->|"uses older shared versions"| NB["Newer broker"]
    NC["Newer client library"] -->|"uses older shared versions"| OB["Older broker"]
    NC -->|"uses newest shared versions"| NB
    OC -->|"uses its own versions"| OB
        
FIG 3 — Clients and brokers of different ages can meet on shared versions

What this policy gives you

Freedom in upgrade order

Teams can upgrade brokers without waiting for every client to be ready, and can upgrade individual applications without waiting for the brokers.

Safe experimentation

A team can try a newer client library with an older test cluster to see whether the application behaves well.

Multi-cluster environments

Applications that connect to several clusters of different ages, such as a mirroring tool, can use one client library that adapts to each cluster.

Limits of compatibility

Compatibility is broad but not infinite. New features require new request versions, so an old broker cannot support a feature that the client needs a newer version to express. For example, if a client wants a feature that only exists in a later version of some request, and the broker’s range stops before that version, the client must either do without the feature or fail with a clear error. The most modern behaviors are only available when both sides are modern enough.

SituationResult
Old client, new brokerWorks using older shared versions; newest broker features are not used
New client, old brokerWorks using the highest versions the old broker understands; some new features unavailable
New client, new brokerWorks with newest versions and all features
Very old client, very new broker whose oldest versions have been removedMay be refused; client must be upgraded
i
Feature availability is a matter of overlap

When a specific feature does not work, ask first whether the highest shared version of the relevant request is new enough to include it. This one question explains a large share of “why does this work on cluster A but not cluster B” puzzles.

13Removing Old Versions: Lifecycle and Deprecation

Supporting every version forever would become a heavy burden. Kafka retires old versions carefully.

The cost of keeping old versions alive

Every supported version is code that must be maintained, tested and secured. Over many years, the number of supported combinations grows very large. Developers must reason about behaviors from many eras, and every new feature must also remain compatible with all the historic paths. This slows development and can hide bugs.

Simple Analogy

A bus company that must keep spare parts and trained mechanics for every model it has ever operated will find its workshop overflowing. Eventually it announces that models older than a certain year will retire, with plenty of notice for passengers to move to newer buses.

Announced, staged removal

1

Propose the removal

A proposal explains which old versions will be dropped, why, and what the minimum supported client will be afterward.

2

Deprecate in advance

Documentation and release notes warn that the versions will disappear in an upcoming major release. Some releases also log warnings when deprecated versions are used.

3

Tie removal to a major release

The actual removal happens in a major release, so operators expect breaking changes and read the upgrade guide carefully.

4

Publish the new floor

The documentation states the oldest client versions that will still work against the new brokers.

A real example of a floor being raised

The Kafka 4.0 release, which also removed ZooKeeper, took a big step here. It dropped support for many of the oldest protocol versions of the main request types, raising the minimum client version that can talk to the broker. The exact minimum differs per request type, but the practical message was clear: clients from many years ago need to be upgraded before, or together with, moving to the newest brokers. Anyone planning such an upgrade should check the official upgrade guide for their exact target release.

!
Audit before you upgrade

Before a major broker upgrade, inventory the client libraries in use across your organization. A single forgotten batch job using a very old library could stop working the moment the old versions are removed.

Client inventory by metrics

Use broker-side metrics that record client software name and version to list every library version currently connected, then chase owners of the oldest ones.

Staging rehearsal

Run representative old clients against a staging cluster on the target release and record any failure before touching production.

14Feature Flags and Finalized Features

Protocol versions tell you what a single broker can understand. Feature flags tell you what the whole cluster has agreed to use.

The gap that versions alone leave

During a rolling upgrade, some brokers run new software while others still run old software. A new broker may be capable of a new behavior, but if it started using it immediately, the still-old brokers could not cooperate. The cluster needs a way to say “everyone has been upgraded, now we may all start using the new behavior together.”

Supported versus finalized

Kafka distinguishes two ideas. A supported feature range describes what one broker’s software is capable of. A finalized feature level describes what the cluster has collectively decided to use right now. Brokers advertise the supported range when they register. Operators then raise the finalized level, and the cluster refuses to raise it beyond what every member supports. The finalized information is stored in the cluster metadata and is also returned by the ApiVersions response, so clients can see it.

Simple Analogy

A group of colleagues each install a new version of a shared calendar app. Everyone can use the new features, but the team agrees to switch on the shared new features only when every person has finished updating. Until then the team keeps using the older common feature set.

The cluster-wide metadata version

The most important finalized feature is the cluster metadata version. In modern KRaft-based clusters, it controls the format of internal metadata records and which cluster behaviors are enabled. It has taken over much of the role that older broker-to-broker protocol settings used to play in ZooKeeper-based clusters, where an operator would edit a configuration setting on each broker in stages.

IdeaQuestion it answersWho decides
Request API version rangeWhich request shapes can this software read?The software release
Supported feature rangeWhich cluster features is this broker capable of?The software release
Finalized feature levelWhich cluster features is the whole cluster using now?The operator, checked by the controller
i
Two-step upgrade habit

Upgrade software first, let it run and prove itself, and only then finalize the new feature level. This gives you a safe window in which you can still roll the software back.

15Broker-to-Broker and Controller Protocols

Clients are not the only speakers. The servers of the cluster talk to each other using the same versioning idea.

Replication uses ordinary requests

When a follower replica copies data from a partition leader, it does so by sending fetch requests, just like a consumer, with a few extra details identifying it as a replica. The follower and the leader must therefore agree on a fetch version, and the same negotiation ideas apply. A cluster with brokers of different ages during an upgrade relies on this to keep replication healthy.

Controller communication

Brokers register with the active controller, send heartbeats and report leadership changes using dedicated request types. Controllers talk to each other to elect a leader and replicate the metadata log. All of these are ordinary versioned APIs with their own key numbers and version ranges, and the same rules of compatibility and deprecation apply to them.

Path

Client to broker

Produce, fetch, metadata, group and administrative requests, negotiated per connection by the client.

Path

Follower to leader

Replication fetches that copy partition data between brokers.

Path

Broker to controller

Registration, heartbeat and partition state change requests.

Path

Controller to controller

Vote, fetch and epoch messages that keep the controller quorum in agreement.

Simple Analogy

In a hospital, doctors talk to patients, doctors talk to nurses and nurses talk to the pharmacy. Each conversation follows its own agreed forms, but they all rely on the same idea of standard, labeled documents that each department can read.

Why internal upgrades need extra care

If a client fails to understand a broker, one application suffers. If a broker fails to understand another broker, replication or coordination may fail for many partitions at once. That is why internal protocol changes are guarded by the feature-finalization mechanism from the previous chapter. New internal behaviors are only switched on after every member of the cluster has been upgraded and the operator has explicitly finalized the new level.

!
Mixed-version clusters are temporary

A cluster that has brokers of two different releases is in a transitional state. Keep that window short, monitor it closely and avoid making unrelated changes while it lasts.

16A Walkthrough: An Older Client Meets a Newer Cluster

Let us follow one realistic connection from start to finish to see every idea working together.

An application team runs a consumer built with a client library from a few years ago. The platform team has just upgraded the cluster to a recent release. Nothing has been changed in the application. Will it still work? Let us watch.

1

Connection opens

The consumer connects to a bootstrap broker. Security setup, such as encryption and authentication, happens according to its configuration.

2

ApiVersions

The client sends the ApiVersions request at the newest version it knows. The recent broker knows that older version too, so it answers with its full table of supported ranges.

3

Overlap per API

For the metadata request, the client’s maximum is lower than the broker’s maximum, so it picks its own maximum. The same happens for fetch, group coordination and offset requests. In every case the highest shared version is the client’s own highest.

4

Metadata and group join

The client asks for metadata at its chosen version, discovers partition leaders and joins its consumer group using the versions negotiated earlier.

5

Fetching data

The client sends fetch requests at its older version. The broker serves the data. If the stored message format is newer than the client can read through that fetch version, the broker converts messages down to the older layout before sending.

6

Steady state

The consumer keeps working, unaware that the cluster is newer. It simply does not benefit from features that only exist in versions it cannot use.

sequenceDiagram
    participant C as Older consumer
    participant B as Recent broker
    C->>B: ApiVersions
    B-->>C: Supported ranges for every API
    C->>C: Select own highest version per API
    C->>B: Metadata at chosen version
    B-->>C: Partition leaders
    C->>B: JoinGroup and SyncGroup at chosen versions
    B-->>C: Assignment
    C->>B: Fetch at older version
    B->>B: Down-convert if needed
    B-->>C: Messages in older layout
        
FIG 4 — Older consumer, recent broker: everything works, features are limited by overlap

What the team should conclude

The application works, but it is leaving benefits on the table and may be causing extra work for the brokers through down-conversion. That is a good reason to schedule an upgrade of the client library during normal maintenance, without any rush or outage. Compatibility gives you the time to do so calmly.

i
Compatibility is a grace period, not a home

Being able to run an old client against a new cluster is a gift of time. Use that time to plan client upgrades rather than treating old clients as permanent residents.

17Errors and Failure Modes

Most version problems show up as a small family of recognizable symptoms.

Unsupported version error

The broker received a request at a version outside its supported range for that API. Usually caused by a custom client that skipped negotiation, a proxy that alters requests, or a client library with a bug in its version selection.

Connection closed right after connecting

A client using a very old or very unusual approach may send something the broker cannot parse at all, causing the broker to close the connection. Also appears when a non-Kafka client, such as a web browser or a health probe speaking another protocol, accidentally connects to the Kafka port.

Feature works on one cluster but not another

The highest shared version differs between the two clusters, so one cluster offers the version that carries the feature and the other does not.

Sudden failures after a major broker upgrade

Very old clients rely on versions that the new release has removed. Errors appear at connection or first request time.

Slow consumers after an upgrade

Old consumers may now trigger heavy down-conversion, raising CPU use on brokers and lowering throughput.

Corrupted or garbled requests

Hand-written protocol clients that mishandle sizes, headers or special cases such as the ApiVersions response header may produce requests the broker cannot make sense of.

SymptomFirst thing to checkLikely fix
Unsupported version messagesThe versions the client sends versus what the broker listsUpgrade or reconfigure the client, remove the interfering proxy
Immediate disconnectsWhat is actually connecting to the portFix the caller or network path
Missing featureHighest shared version of the relevant requestUpgrade the side with the lower maximum
Failures after major upgradeClient library ages versus the new minimumUpgrade clients before or with the brokers
Higher broker CPU with old consumersDown-conversion activityUpgrade consumers, avoid old message formats
!
Do not guess versions in custom code

If you implement a client yourself, always perform the ApiVersions exchange, respect the ranges and handle the fallback rule. Hard-coding a version that works today on your test cluster is a reliable way to break after the next upgrade.

18Security Considerations

Protocol versioning touches security in several quiet but important ways.

Authentication has its own versioned requests

Security mechanisms such as SASL use dedicated requests: one to choose a mechanism and one to carry the authentication exchange. These requests have versions, and the way the exchange is wrapped depends on the version. Older versions sent the authentication tokens in a raw form outside of normal request framing, while newer versions wrap them as ordinary requests and responses. Clients and brokers must agree on this, which is one more reason the ApiVersions exchange happens before authentication.

Risk

Very old versions may lack protections

Older request versions predate fixes and hardening. Keeping them enabled forever preserves their weaknesses.

Risk

Oversized or malformed requests

Every server that parses network input must guard against absurdly large sizes. Brokers enforce a maximum request size for this reason.

Risk

Unauthenticated exposure

Before authentication completes, only a small set of requests should be allowed, such as ApiVersions and the authentication requests themselves.

Risk

Information leaks through the handshake

Capability tables reveal software age. Exposing brokers to untrusted networks can help an attacker choose known weaknesses.

Simple Analogy

A building’s front desk will tell any visitor the opening hours, which is harmless, but will not open any inner doors until the visitor shows valid identification. The ApiVersions exchange is like asking about opening hours: allowed before identity checks, but only that and the identity checks themselves.

Practical protective measures

Encrypt all traffic

Use TLS so that requests, responses and any authentication material cannot be read or modified on the network.

Retire old client libraries

Use the version floor raised by newer releases as a chance to remove clients that rely on weaker or unmaintained protocol paths.

Restrict who can reach brokers

Use network policy so that only expected clients and cluster members can connect. Keep brokers off untrusted networks.

Limit request sizes

Keep the maximum request size at a value that fits your legitimate traffic, so accidental or hostile giant requests are refused early.

19Monitoring, Logging and Metrics

You can only manage version diversity if you can see it.

Questions worth answering with metrics

Question 1

Which versions of each request are actually in use?

Broker request metrics can be broken down by request type and by version, showing how much traffic still uses old versions.

Question 2

Which client libraries are connected?

Connection metrics grouped by client software name and version reveal the libraries in use across the organization.

Question 3

Is down-conversion happening?

Broker metrics about message conversion show how much conversion work is being done and whether it is growing.

Question 4

Are there version-related errors?

Error rates per request type, including unsupported-version errors, should normally be near zero.

Question 5

Are brokers on the same release?

Inventory tooling and broker version information show whether a mixed-version window is open and how long it has lasted.

Question 6

What is the finalized feature level?

The cluster’s finalized features should match what you intend after every upgrade.

SignalWhere it appearsHealthy signWarning sign
Requests per second by request type and versionBroker request metricsTraffic concentrated on recent versionsLarge share on very old versions before a planned removal
Connections by client softwareBroker connection metricsFew distinct, recent library versionsLong tail of ancient libraries
Message conversion rateBroker conversion metricsClose to zeroSteady or rising conversion work
Unsupported version errorsBroker and client logs, error metricsNoneAny sustained occurrence
Finalized feature levelsCluster feature toolingMatches planLagging behind after upgrade
i
Trend beats snapshot

Plot the share of traffic by client library version over months. A healthy organization shows old versions shrinking over time. A flat long tail means nobody owns the upgrade work.

Logs that help

Broker logs record connection problems, unsupported requests and authentication failures. Client logs commonly print the versions negotiated when connecting, or provide a debug setting that does. When investigating, collect the client’s negotiated versions and the broker’s supported ranges side by side, and the mismatch usually becomes obvious.

20Inspecting Versions With Built-in Tools

Kafka ships tools that let you look at versions directly, with no guesswork.

Tool

Broker API versions tool

Connects to a broker and prints every request type it supports, with the minimum and maximum version of each. This is the quickest way to see what a cluster accepts.

Tool

Features tool

Shows supported and finalized feature levels for the cluster, including the metadata version, and can raise a level.

Tool

Protocol documentation

The official documentation lists every request type, its versions and the fields present in each version.

Tool

Network packet analyzers

General network analysis tools can decode Kafka traffic and show the API key, version and correlation identifier of each request on the wire.

Practical uses

Comparing two clusters

Run the broker API versions tool against each cluster and compare the maximum version of the request a feature depends on.

Confirming a rolling upgrade

After upgrading, ask each broker for its supported versions and confirm they match the new release before finalizing features.

Diagnosing a custom client

Capture traffic and examine the first few requests. If the client is skipping ApiVersions or sending a version outside the broker’s range, the capture will show it.

i
Make it a habit

Add a version report to your upgrade checklist. Produce it before and after each change and store it with the change record, so future incidents can be compared against a known good state.

21Reading a Version Table Like an Expert

The official protocol documentation is full of tables. Learning to read them turns them from a wall of text into a quick answer machine.

Anatomy of an API entry

For every request type, the documentation shows the API key, the name, a description of what it does and the list of versions. Under each version you find the exact fields of the request and of the response, in order. Some fields appear only in later versions. Some are marked as optional, and some are described as nullable, meaning they may hold an explicit “nothing” value in certain versions.

Element

Valid versions

The range of versions that exist for the request. The lower end may have been raised over time as very old versions were retired.

Element

Flexible versions

The point from which a request uses compact encodings and tagged fields. Everything from that version onward uses the newer header.

Element

Field version ranges

For each field, the versions in which it exists. A field with a range that starts at version five simply does not exist in versions zero through four.

Element

Nullable versions

The versions in which a field may be explicitly empty. Outside those versions, an empty value is not allowed.

Element

Default values

What a field means when it is absent in an older version, so that old and new senders behave consistently.

Element

Error codes

The set of numeric codes a response may carry. Some codes are only returned by later versions.

Simple Analogy

Reading a train timetable takes practice. Once you know that a symbol means “weekends only” and another means “changes at the next station”, you can plan any trip in seconds. Protocol tables have their own symbols, and learning them once pays off for years.

A method for answering “can I use this feature”

1

Find the request

Identify which request type carries the feature, such as fetch for consumer isolation settings or produce for transactional identifiers.

2

Find the field

Locate the field that carries the feature and note the first version in which it appears.

3

Check the broker range

Use the broker API versions tool to find the broker’s maximum version of that request.

4

Check the client range

Check the client library’s documentation or release notes for the highest version it implements.

5

Compare

If the smaller of the two maximums is at or above the version that introduced the field, the feature is available. Otherwise, one side needs an upgrade.

i
The minimum-of-maximums rule

The usable version for any request is the smaller of the two maximums, as long as it is not below both minimums. Remembering this one sentence lets you answer most compatibility questions without any tooling.

22Features That Depend on Specific Versions

Many familiar Kafka features exist only because a particular request grew a new version.

It is instructive to see how well-known capabilities map onto protocol evolution. The exact version numbers differ between requests and are best confirmed in the documentation, but the pattern is consistent: a capability arrives with a new request version or a new request type, and older versions simply cannot express it.

CapabilityWhat changed on the wireConsequence for old peers
Message timestampsA newer message format with a timestamp per record, carried by newer produce and fetch versionsOld peers cannot use time-based features
Record headers and batchesThe second message format and produce and fetch versions that carry itOld consumers need down-conversion to read modern data
Idempotent producersProducer identity fields and sequence numbers in produce requests, plus a request to obtain a producer identityOlder brokers cannot deduplicate retries
TransactionsNew request types for beginning, extending and completing transactions, plus isolation settings on fetchOlder brokers do not list these requests at all
Incremental fetch sessionsFetch versions that let a consumer send only changes to its partition listOld peers send the full list every time, costing bandwidth
Topic identifiersUnique identifiers for topics carried by newer metadata and fetch versionsOld peers refer to topics by name only
Rack-aware readsA field on fetch describing the client’s rackOld clients cannot ask for nearby replicas
Client software reportingExtra fields in a newer ApiVersions requestOld clients remain anonymous in broker metrics
Simple Analogy

A restaurant may add a new section to its order form for allergies. Customers who use the old form can still order, but the kitchen cannot use allergy information from them. The new capability only works when the customer uses the new form.

What to notice in this list

New capabilities either extend a request or introduce a new one

Extending a request means old peers keep working with reduced features. Introducing a new request means old brokers simply do not advertise it, and clients must check for its presence before relying on it.

Capabilities are cumulative

A modern client on a modern broker uses many of these features together. On an older broker, the same client silently uses fewer of them.

Some features need both sides and the cluster

A few features additionally need a cluster-level feature flag to be finalized. Both request versions and the finalized level must allow the behavior.

!
Silent degradation

When a client cannot use a feature because of a low shared version, it may quietly fall back to simpler behavior rather than failing loudly. A stronger guarantee that your application assumes, such as no duplicate messages, might therefore not be in force. Check the negotiated versions when correctness depends on a feature.

23Group Coordination and Its Own Protocol Versions

Consumer groups add a second layer of agreement: the members of a group must also agree with each other.

Two kinds of compatibility in one system

So far we have looked at client-to-broker compatibility. Consumer groups add another dimension. The members of a group must agree on how partitions are divided among them. In the classic design, the members themselves run the assignment logic, and they announce which assignment strategies they support when they join. Because the group may contain members running different library versions, a common strategy must be chosen that every member understands.

Simple Analogy

A group of friends splitting a restaurant bill must agree on a method: split equally, or each pay for what they ordered. Each friend states which methods they are happy with, and the group picks one everybody accepts.

The classic rebalance conversation

1

Find the coordinator

A member asks a broker which broker acts as the group coordinator for its group.

2

Join with supported strategies

Each member sends a join request listing the assignment strategies it supports, along with its subscription details.

3

Leader chooses

The coordinator picks one member as group leader and a strategy that all members support. The leader computes the assignment.

4

Sync the assignment

The leader sends the assignment to the coordinator using a sync request, and every member receives its own share.

5

Heartbeat

Members send regular heartbeats to prove they are alive. Missing heartbeats triggers a new rebalance.

The newer group protocol

Recent Kafka releases introduced a redesigned consumer group protocol in which the broker side takes over computing assignments, and members communicate through a dedicated heartbeat request. This reduces the disruption of rebalances and simplifies client logic. It is enabled through a combination of broker capability, cluster feature level and client configuration, so adopting it is a good example of how several kinds of versions must line up before a new behavior is used.

Classic protocol

  • Widely supported by every client library of every age
  • Assignment logic runs inside the clients
  • Rebalances may pause all members briefly
  • Custom assignment strategies are possible

Newer protocol

  • Requires recent brokers and recent clients, plus the right settings
  • Assignment is computed on the broker side
  • Rebalances are more incremental and less disruptive
  • Client libraries must add explicit support before it can be used
i
Mixed groups need a common denominator

If one member of a group uses an old library, the whole group must fall back to behavior that member understands. Upgrading consumers as a whole group, rather than one at a time over months, avoids long periods stuck on the oldest common behavior.

24Client Libraries Across Languages

Kafka has no single official client for every language. Each library implements the protocol on its own schedule.

A diverse ecosystem

The Java client is maintained together with the Kafka broker and usually receives new protocol versions first. Libraries for other languages are built by other communities and companies. Some are thin wrappers around a shared native library, others are entirely separate implementations. Each decides how quickly to adopt new request versions and how much of the protocol to implement at all.

Style

Native wrapper libraries

Several languages share one high-performance native implementation. Their protocol support tracks that shared library, so upgrading it upgrades many languages at once.

Style

Pure-language implementations

Written entirely in one language for easy deployment. Their protocol coverage depends on how actively they are maintained.

Style

Framework-embedded clients

Stream processing and connector frameworks bundle a specific client version. Their effective protocol support is that of the bundled version.

Style

Protocol-aware proxies

Gateways that let simple clients talk to Kafka over other protocols. Their support depends on the gateway’s own version.

Version fallback settings

Because some old brokers could not answer the ApiVersions request, certain libraries historically offered settings such as a switch to disable the request and a fallback broker version to assume in its place. In today’s environment, where nearly all brokers support the request, these settings should normally be left alone, and depending on them is a sign of an outdated deployment.

Simple Analogy

Translators come in different kinds. Some are employed by the embassy and get every new vocabulary update immediately. Some are freelancers who learn new terms only when they have time. When accuracy matters, you check which kind of translator you have hired.

Choosing a library

Prefer libraries that are actively maintained, publish which protocol versions they implement, and document their compatibility with broker releases.

Understanding embedded versions

When a framework bundles a client library, upgrading the framework is how you upgrade the client. Read the framework’s release notes to learn the bundled version.

Language-specific behavior differences

Two libraries speaking the same versions may still expose different settings and defaults. Test behavior, not only connectivity.

!
Transitive dependencies

A library you never chose directly may bring an old Kafka client along as a dependency. Inspect your dependency trees during audits so hidden old clients do not surprise you on upgrade day.

25Testing Compatibility Before It Bites

Compatibility that is never tested is only a hope. A little structure turns hope into evidence.

Build a compatibility matrix

List the client libraries and versions that matter in your organization on one axis and the broker releases you run or plan to run on the other. Each cell answers a question: does a representative application using this library work correctly against this broker release, including the features it relies on? Even a small matrix, filled in honestly, reveals risks that no amount of reading can.

Client libraryCurrent broker releaseNext broker releaseNotes to record
Oldest supported libraryTested, worksTo be testedFeatures lost, conversion activity
Most common libraryTested, worksTo be testedPerformance and latency figures
Newest libraryTested, worksTo be testedNew features available
Framework-bundled librariesTested, worksTo be testedBundled version numbers

What to test in each cell

1

Connect and authenticate

Confirm the handshake, negotiation and security exchange succeed cleanly.

2

Produce and consume

Send a realistic volume and verify that every message arrives, in order, with headers and timestamps intact.

3

Exercise the group protocol

Start, stop and restart members and confirm that rebalances complete and no partitions are orphaned.

4

Exercise special features

If you use transactions, idempotence, quotas or access control, test each feature specifically, and confirm the guarantees still hold.

5

Inject failures

Restart brokers one at a time during the test to imitate a rolling upgrade, and watch for errors or stalls.

6

Measure

Record throughput, latency and broker CPU, watching especially for jumps that suggest message conversion.

Simple Analogy

Before opening a new bridge to traffic, engineers drive test vehicles of every weight class across it. Testing every kind of client against every kind of broker is the same idea, done before real traffic depends on it.

i
Automate what you can

Keep the compatibility tests in a pipeline that can be re-run against each new broker release candidate. What is automated actually gets run, and what gets run finds problems while they are still cheap to fix.

26The Rolling Upgrade Playbook

Everything in this tutorial exists so that upgrades can be done calmly, one piece at a time.

1

Read the upgrade notes

Study the release notes and upgrade guide for the target version. Look for removed protocol versions, changed defaults and any required intermediate releases.

2

Inventory clients

Use metrics to list client libraries and versions. Compare them with the minimum supported by the target release.

3

Upgrade stragglers first

If any clients are below the new floor, upgrade them before touching the brokers.

4

Rehearse in staging

Run the same upgrade on a staging cluster with representative clients and watch for errors and performance shifts.

5

Upgrade controllers, then brokers

Roll the new software through the controller quorum and then through the brokers, one node at a time, waiting for full health between steps.

6

Observe

Watch error rates, latency, conversion activity and client connectivity across a full business cycle.

7

Finalize new features

Only after stability is proven, raise the finalized feature level to unlock the new cluster behaviors.

8

Upgrade clients on their own schedule

Application teams move to newer client libraries at a comfortable pace, gaining access to newer request versions and features.

flowchart TB
    A["Read upgrade notes"] --> B["Inventory client libraries"]
    B --> C{"Any client below new floor?"}
    C -->|"yes"| D["Upgrade those clients first"]
    C -->|"no"| E["Rehearse in staging"]
    D --> E
    E --> F["Roll controllers then brokers"]
    F --> G["Observe through a full cycle"]
    G --> H["Finalize feature level"]
    H --> I["Clients upgrade at their own pace"]
        
FIG 5 — A safe path through a Kafka upgrade
Simple Analogy

Renovating a busy shop while it stays open: you check the plan, move fragile items first, rehearse the trickiest part in a spare room, renovate one aisle at a time, watch that customers are still happy, and only then remove the temporary barriers.

!
Never skip a required stepping stone

Some major upgrades require passing through a specific intermediate release. Jumping past it can leave the cluster in a state that the target release does not know how to read. The upgrade guide states these requirements explicitly.

27Advantages and Trade-offs

Kafka’s versioning approach has made it easy to evolve, but it is not free of cost.

Advantages

  • Rolling upgrades of brokers without stopping clients
  • Clients and brokers can be upgraded in either order within supported ranges
  • Each API evolves independently, so changes are small and focused
  • Schemas generate code, reducing hand-written parsing errors
  • Flexible versions make many future additions cheap
  • Client software names and versions can be observed for planning

Disadvantages / Trade-offs

  • A large matrix of API and version combinations to test and document
  • Supporting old versions slows down development and can hide bugs
  • Old clients can force costly down-conversion on brokers
  • Behavior differences between versions can confuse troubleshooting
  • Major releases eventually raise the floor, forcing client upgrades
  • Custom client authors must implement negotiation correctly

Comparison with other approaches

ApproachHow it worksStrengthWeakness
One global protocol versionAll messages share a single version numberSimple to reason aboutAny change forces everyone to move; no partial upgrades
Per-request versions with negotiation, as in KafkaEach request type has its own range, chosen per connectionFine-grained evolution and mixed-age fleetsLarger testing matrix
Self-describing text formatsMessages carry field names, receivers ignore unknown onesVery tolerant of changeLarger and slower to parse
Generated binary schemas with optional fieldsFields are numbered, unknown ones skippedCompact and tolerantRequires discipline in field numbering and defaults
“Kafka chose fine-grained versions and a small negotiation step, and paid for it with a bigger test matrix.”

These habits keep upgrades boring, which is exactly what you want.

Practice 1

Use official client libraries

Well-maintained libraries implement negotiation, fallbacks and special cases correctly. Writing your own client is rarely worth the risk.

Practice 2

Keep client libraries reasonably current

Schedule regular library upgrades so no application drifts far behind the floor of supported versions.

Practice 3

Track client versions centrally

Feed the client software metrics into a dashboard owned by the platform team, with a list of the applications behind each library version.

Practice 4

Announce a deprecation calendar

Tell application teams months ahead which client versions will stop working, and when.

Practice 5

Separate software rollout from feature finalization

Finish and validate the software upgrade first. Finalize new features later, as a separate reversible-in-spirit step.

Practice 6

Test with the oldest client you support

Keep a small test application built with your oldest supported library and run it against every candidate cluster release.

Practice 7

Record a version report at each change

Store the API version ranges and feature levels before and after every upgrade for later comparison.

Practice 8

Avoid version-specific hacks in applications

Applications should not depend on undocumented behavior of one version. Rely on documented, supported behavior.

i
Make the platform team the owner

Client version drift is a shared problem that nobody owns unless someone is assigned. Make it explicit that the platform team measures it and application teams act on it.

29Anti-patterns to Avoid

Each of these looks harmless until an upgrade day arrives.

ANTI-PATTERN-01 Avoid
Problem

Leaving client libraries untouched for years because “they still work”.

Why It’s Harmful

The gap to the supported floor grows silently. When a major broker upgrade raises the minimum, dozens of applications may break at once, with the upgrade blamed for problems that were building for years.

Correct Approach

Upgrade client libraries on a regular cadence, and monitor the age distribution of client versions.

ANTI-PATTERN-02 Avoid
Problem

Hard-coding protocol versions in a custom client or proxy.

Why It’s Harmful

The code works against today’s brokers and fails as soon as ranges change. It also prevents automatic use of newer, better versions.

Correct Approach

Always perform the ApiVersions exchange and pick the highest shared version for each request type.

ANTI-PATTERN-03 Avoid
Problem

Finalizing new cluster features in the same step as the software upgrade.

Why It’s Harmful

Once features are finalized, rolling the software back may no longer be safe. You lose your safety window at the moment when unexpected problems are most likely to appear.

Correct Approach

Upgrade the software, observe for a full cycle, and then finalize features as a separate, deliberate change.

ANTI-PATTERN-04 Avoid
Problem

Ignoring down-conversion cost.

Why It’s Harmful

Old consumers quietly force brokers to convert message formats and lose zero-copy transfer, raising CPU use and lowering throughput. The cost is spread across the cluster and hard to attribute.

Correct Approach

Monitor conversion metrics and upgrade the consumers responsible.

ANTI-PATTERN-05 Avoid
Problem

Putting network devices or proxies that rewrite or inspect Kafka traffic without understanding versions.

Why It’s Harmful

A device that does not understand newer versions may drop, truncate or corrupt requests, producing baffling intermittent failures.

Correct Approach

Use transport-level tools that treat Kafka traffic as opaque bytes, or use proxies that explicitly track Kafka protocol versions.

ANTI-PATTERN-06 Avoid
Problem

Skipping the release notes because “it is only a minor bump”.

Why It’s Harmful

Deprecations, changed defaults and behavior differences are announced in the notes. Missing them turns a routine change into an incident.

Correct Approach

Read the notes for every release between your current and target version, and record the items that affect you.

30Real-World and Industry Perspectives

Protocol versioning shows up in daily practice across the Kafka ecosystem.

Managed cloud Kafka services

Cloud providers regularly upgrade brokers behind the scenes. Their customers can keep using existing client libraries because of the compatibility rules, and providers publish the minimum supported client versions well in advance.

Multi-language client ecosystem

Libraries in many programming languages implement the same protocol independently. Each must negotiate versions and handle special cases. This is why interoperability testing and the public protocol documentation matter so much.

Replication between clusters

Tools that mirror data between clusters often connect to clusters of different ages, sometimes from different providers. Negotiation lets one tool serve both, using the best version each cluster supports.

Data platforms and connectors

Connector frameworks embed client libraries. Upgrading the framework upgrades every connector at once, which is why platform teams track the embedded library version carefully.

Protocol-aware proxies and gateways

Some products sit between clients and brokers to add features such as routing, quotas or multi-tenancy. To work correctly they must understand the protocol versions and keep up with new ones, which is a significant engineering commitment.

Regulated environments

Organizations under strict controls often insist on documented upgrade plans. The predictable versioning policy makes it possible to write such plans with confidence.

Gradual
upgrades instead of big-bang moves
Measured
client drift through metrics
Planned
removals tied to major releases

31Common Myths About Kafka Protocol Versions

A few persistent misunderstandings cause a surprising number of upgrade mistakes.

MYTH-01 Incorrect
Problem

“The client and broker must run the same Kafka version.”

Why It’s Harmful

Believing this makes teams postpone upgrades in fear or, worse, force risky simultaneous upgrades of everything at once.

Correct Approach

Clients and brokers only need overlapping version ranges for the requests they use. Different releases can and do work together within supported limits.

MYTH-02 Incorrect
Problem

“If a connection works, every feature works.”

Why It’s Harmful

A connection can succeed using low shared versions while silently lacking newer capabilities, leading to surprises when an application assumes a guarantee that is not in force.

Correct Approach

Verify that the negotiated versions include the ones that carry the features your application depends on.

MYTH-03 Incorrect
Problem

“Old clients will work with newer brokers forever.”

Why It’s Harmful

Compatibility is generous but not endless. Major releases raise the floor, and a client ignored for years may suddenly be refused.

Correct Approach

Treat backward compatibility as a grace period and keep client libraries on a regular upgrade schedule.

MYTH-04 Incorrect
Problem

“Protocol versions and message format versions are the same thing.”

Why It’s Harmful

Confusing the two leads to wrong diagnoses, such as blaming a request version for a storage layout issue, and to mistaken assumptions about which upgrade will fix a problem.

Correct Approach

Remember the layers: request versions shape the conversation, message formats shape stored records, and feature levels describe cluster-wide agreement.

i
Replace myths with questions

When something behaves oddly, ask three questions instead of trusting a myth: which request is involved, what is the highest version both sides share for it, and does that version carry the behavior I expect?

32Frequently Asked Questions

Q1Is there one protocol version for the whole Kafka connection?

No. Every request type has its own version numbering. A single connection can carry requests of many types at many different versions.

Q2Who chooses the version to use, the client or the broker?

The client chooses. The broker advertises the range it supports for each request type, and the client picks the highest version that both sides understand.

Q3Why does the ApiVersions response use the oldest header?

Because it is the first exchange on a connection, before anything is known about the peer. Using the simplest header lets even very old clients read at least the essential part, including an error and the supported range.

Q4What if the broker does not support the ApiVersions version the client sends?

The broker replies in the oldest response format with an unsupported-version error and includes its supported table, so the client can retry with a version the broker understands.

Q5What is the difference between a request version and a message format version?

The request version describes the layout of the request and response bodies. The message format version describes the layout of the records inside a batch. They evolve separately.

Q6What is down-conversion and why does it matter?

It is the broker converting stored messages into an older layout for an old consumer. It costs CPU and memory and stops the broker from using its most efficient data path, so many old consumers can noticeably reduce broker performance.

Q7What are tagged fields?

They are optional fields at the end of a flexible-version message, each with a tag number and a size. A receiver that does not recognize a tag can skip it safely, which makes many changes possible without a new version.

Q8Can an old client always talk to a new broker?

Usually yes, as long as the old versions it uses are still supported. Major releases can remove very old versions, in which case very old clients must be upgraded first.

Q9Can a new client always talk to an old broker?

Generally yes within a broad range, using the highest versions the old broker supports. Features that need newer request versions will not be available.

Q10How is a feature level different from a request version?

A request version range describes what a piece of software can read. A finalized feature level describes what the whole cluster has agreed to use. Operators raise the latter deliberately, after all members are upgraded.

Q11How can I find out which client libraries are connecting to my cluster?

Recent brokers record the client software name and version reported during the handshake and expose it in metrics and logs. Use that to build an inventory.

Q12Why do some features work on one cluster but not another?

Because the highest shared version of the relevant request may differ. One cluster may offer the version that carries the feature and the other may not.

Q13Should I write my own Kafka client?

Rarely. The protocol has many special cases, such as the header rule for ApiVersions and fallback behavior, and official libraries have already solved them. If you must, follow the public protocol documentation closely and test against many broker versions.

Q14Does raising the metadata version affect clients directly?

Not directly. It is a cluster-level decision about internal behavior. Clients continue to negotiate request versions with brokers as before, though new cluster capabilities may later show up as newer version ranges or features.

Q15What is the single best practice to avoid version surprises?

Measure client library versions continuously and upgrade them steadily, so no application is ever far behind the versions your brokers support.

Q16Does the broker ever push a version choice onto the client?

No. The broker only publishes what it accepts. Every decision about which version to send is taken inside the client library, which is why a buggy or outdated library is a common source of version errors.

Q17Why does a fetch from a follower replica use the same mechanism as a consumer?

Kafka reuses one well-tested request for both jobs, adding a few identifying details for replicas. This means replication between brokers of different ages relies on the same negotiation rules that clients use, which keeps rolling upgrades of the cluster safe.

Q18Can I see which version a specific request used?

Yes. Every request header carries the version, so packet captures show it directly, and broker request metrics can be grouped by request type and version. Client debug logging often prints the negotiated versions at connection time.

Q19What happens if a proxy sits between my client and the broker?

A transparent network proxy that only forwards bytes is fine. A proxy that understands Kafka requests must itself support the versions in use, otherwise it may reject or damage newer requests. Always check the proxy’s supported range.

Q20Is a higher version always better?

Generally newer versions carry more capability and better efficiency, but they are not automatically better for every application. The right approach is to let the client choose the highest shared version, and to test that the resulting behavior matches your expectations.

Q21Why are there so many small version bumps?

Because each version is frozen once released, even a tiny change becomes a new version. The many small steps are a deliberate price paid for never altering the meaning of something that peers already depend on.

Q22What should I read before every upgrade?

The release notes and the upgrade guide for every release between your current and target version, with special attention to removed protocol versions, changed defaults and any required intermediate release.

33Summary and Key Takeaways

Kafka’s protocol versioning is a quiet piece of engineering that allows an enormous, mixed-age fleet of clients and brokers to work together. Every request type carries its own version, every broker publishes the ranges it accepts, and every client chooses the highest version both sides understand, starting with a special handshake that even the oldest software can begin. Flexible encodings, careful compatibility rules, generated code, feature flags and a disciplined deprecation process complete the picture. Understanding these pieces lets you plan upgrades with confidence, diagnose odd compatibility errors quickly and keep your platform healthy for years.

Key Takeaways

  • Versions are per request type — each API key has its own version range, and a version number is meaningful only together with its API key.
  • The client negotiates — brokers advertise minimum and maximum versions through the ApiVersions request, and the client picks the highest shared version for every API.
  • The handshake has a safety net — the ApiVersions response uses the oldest header so that even an unsupported request can be answered in a form the client can read and retry.
  • Flexible versions make change cheaper — tagged fields and compact encodings let many additions ride along without breaking older readers.
  • Three layers of versioning exist — request versions, message format versions and cluster feature levels each answer a different question and evolve separately.
  • Compatibility is broad but bounded — old and new clients and brokers interoperate within supported ranges, and major releases eventually raise the floor.
  • Measure and plan — track client library versions and down-conversion, upgrade stragglers first, and finalize new features only after the software rollout has proven stable.