Skip to content

AxonOps — AI-Native Control Plane for Open Source Data Platforms

Kafka Protocol Message Format

This document specifies the message framing, request/response structure, and header formats used in the Apache Kafka binary wire protocol. All communication between clients and brokers uses this message format over TCP connections.


Every Kafka protocol message is length-prefixed:

Message => Size:INT32 Content:BYTES[Size]
Kafka message frame with size prefix and contentKafka message frame with size prefix and contentKafka Message FrameSize(4 bytes)Content(Size bytes)INT32: Total byte countof Content (excludesSize field itself)
FieldTypeDescription
SizeINT32Byte length of Content (does not include the Size field itself)
ContentBYTESHeader + Body (request or response)
ConstraintDefaultConfiguration
Maximum request size100 MBsocket.request.max.bytes (broker)
Maximum response sizeRequest-boundedfetch.max.bytes and max.partition.fetch.bytes (client), message.max.bytes (broker)
Minimum frame sizeHeader-only4 bytes (response header v0), 10 bytes (request header v1), 11 bytes (request header v2)

Size Validation

Implementations must validate the Size field before allocating buffers. A Size value exceeding configured limits must result in connection termination.


Request => Size RequestHeader RequestBody
Size => INT32
RequestHeader => api_key api_version correlation_id client_id [tagged_fields]
api_key => INT16
api_version => INT16
correlation_id => INT32
client_id => NULLABLE_STRING
tagged_fields => TaggedFields (header v2+ only)
RequestBody => (API-specific fields)
Request frame layout: size, header, and body-right-> BodyRequest frame layout: size, header, and bodyRequest FrameRequest HeaderSize (4B)Request Body(API-specific)api_key (2B)api_version (2B)correlation_id (4B)client_id (var)tagged_fields (var)Only present inheader version 2+
Header VersionKafka VersionTagged FieldsUsage
00.8.x - 3.xControlledShutdownRequest v0 only (removed in 4.0)
10.9.0+Most non-flexible APIs
22.4.0+Flexible API versions

Identifies the API being invoked.

RequirementLevel
Must be a valid API key recognized by the brokermust
Must correspond to an implemented APImust

See Protocol APIs for the complete API key reference.

Specifies the version of the API schema to use.

RequirementLevel
Must be within broker's supported range for the APImust
Should be the highest mutually supported versionshould
Must not be negativemust

Version Negotiation:

API version negotiation between client and brokerClientBrokerClientClientBrokerBrokerApiVersionsRequest (v0-v3)ApiVersionsResponse{api_key: [min, max], ...}Client stores supportedversion ranges per brokerProduceRequest(v9)Selected version withinbroker's supported rangeProduceResponse(v9)

A client-generated identifier for matching responses to requests.

RequirementLevel
Must be unique among in-flight requests on the connectionmust
Should increase monotonicallyshould
May wrap around after reaching INT32 maximummay

Behavioral Contract:

  • The broker must echo the exact correlation_id in the response
  • The client must use correlation_id to match responses to pending requests
  • Duplicate correlation IDs among in-flight requests result in undefined behavior

An optional identifier for the client application.

RequirementLevel
Should identify the client applicationshould
May be nullmay
Used for logging, metrics, and quota enforcement-

Common Patterns:

PatternExamplePurpose
Application nameorder-serviceService identification
Instance IDorder-service-pod-1Instance tracking
Consumer grouporder-processor-groupGroup correlation

Optional tagged fields for forward compatibility (header v2+ only).

Currently defined request header tags: None


Response => Size ResponseHeader ResponseBody
Size => INT32
ResponseHeader => correlation_id [tagged_fields]
correlation_id => INT32
tagged_fields => TaggedFields (header v1+ only)
ResponseBody => (API-specific fields)
Response frame layout: size, header, and body-right-> BodyResponse frame layout: size, header, and bodyResponse FrameResponse HeaderSize (4B)Response Body(API-specific)correlation_id (4B)tagged_fields (var)Only present inheader version 1+
Header VersionKafka VersionTagged FieldsUsage
00.8.0+Non-flexible APIs
12.4.0+Flexible API versions

Echo of the correlation_id from the request.

RequirementLevel
Must exactly match the request's correlation_idmust
Must not be modified by the brokermust

Optional tagged fields for forward compatibility (header v1+ only).

Currently defined response header tags: None


The request header version is determined by the API and its version:

Selecting request and response header versionsSelecting request and response header versionsDetermine API key and versionAPI has flexible versions?yesnoSelected version >= first flexible version?yesnoUse Request Header v2Use Response Header v1Use Request Header v1Use Response Header v0API is ControlledShutdown?yesnoUse Request Header v0Use Request Header v1Use Response Header v0

Each API defines which versions are "flexible" (use compact encodings and tagged fields):

APIFirst Flexible VersionCurrent Max Version
Produce913
Fetch1218
ListOffsets611
Metadata913
OffsetCommit810
OffsetFetch610
FindCoordinator36
JoinGroup69
Heartbeat44
LeaveGroup45
SyncGroup45
DescribeGroups56
ListGroups35
CreateTopics57
DeleteTopics46
ApiVersions34

Client-side correlation ID lifecycleClient-side correlation ID lifecycleGenerate IDTrack PendingSend RequestReceive ResponseMatch & CompleteMap<Integer, PendingRequest>key = correlation_idnew requestcorrelation_id = next()add to pending mapwait for responselookup by correlation_idcomplete future/callback
StrategyProsCons
Monotonic counterSimple, predictableRequires synchronization
AtomicIntegerThread-safeWraps at INT32_MAX
Per-connection counterNo global syncSimpler debugging

Implementation Example:

// Thread-safe correlation ID generator
private final AtomicInteger correlationIdCounter = new AtomicInteger(0);
public int nextCorrelationId() {
return correlationIdCounter.getAndIncrement();
}
ScenarioClient Action
Response receivedRemove from pending, complete request
Timeout exceededRemove from pending, fail request
Connection closedFail all pending requests
Duplicate correlation_id receivedLog warning, ignore duplicate

GuaranteeLevel
Requests from a single connection must be processed in ordermust
Responses must be sent in request ordermust
Responses may not be reorderedmust not
Responses returned in request order on a connectionClientBrokerClientClientBrokerBrokerRequest(corr=1)Request(corr=2)Request(corr=3)Process in order:1, 2, 3Response(corr=1)Response(corr=2)Response(corr=3)Responses sent insame order as requests

Clients may send multiple requests without waiting for responses (pipelining):

ConfigurationDescription
max.in.flight.requests.per.connectionMaximum concurrent requests
Default value5
Ordering guarantee with idempotencePreserved (Kafka 0.11+)

Ordering Without Idempotence

Without idempotent producers (enable.idempotence=false), retry of a failed request may cause message reordering if max.in.flight.requests.per.connection > 1.


The ApiVersions API has special handling for bootstrap:

BehaviorDescription
Pre-authenticationBroker must respond before SASL authentication
Version toleranceBroker should accept any version (0-4)
Header versionUses request header v1 (or v2 for v3+)
ApiVersions exchange before SASL authenticationClientBrokerClientClientBrokerBrokerConnection EstablishedApiVersionsRequest(v0)No authentication yetApiVersionsResponseReturns supported API versionsOptional: SASL AuthenticationSaslHandshakeRequestSaslHandshakeResponseSASL exchangeNormal OperationMetadataRequestMetadataResponse

Produce requests with acks=0 have special response handling:

BehaviorDescription
No response sentBroker must not send a response
No correlation trackingClient must not wait for response
Fire-and-forgetDelivery not confirmed

acks=0 Semantics

With acks=0, the client must not allocate a pending request entry or wait for a response. Message delivery is not guaranteed and cannot be verified.


Request: Metadata, version 0, correlation_id=1, client_id="test"
Hex dump:
00 00 00 12 // Size: 18 bytes
00 03 // api_key: 3 (Metadata)
00 00 // api_version: 0
00 00 00 01 // correlation_id: 1
00 04 // client_id length: 4
74 65 73 74 // client_id: "test"
FF FF FF FF // topics: null (all topics)
Response: Metadata v0, correlation_id=1
Hex dump:
00 00 00 0C // Size: 12 bytes
00 00 00 01 // correlation_id: 1
00 00 00 00 // brokers: empty array
00 00 00 00 // topics: empty array
Request: Produce, version 9, correlation_id=42
Hex dump:
00 00 00 XX // Size
00 00 // api_key: 0 (Produce)
00 09 // api_version: 9
00 00 00 2A // correlation_id: 42
00 05 // client_id length: 5
70 72 6F 64 31 // client_id: "prod1"
00 // tagged_fields: none (header v2)
... // request body (flexible encoding)

Error ConditionBroker Response
Size exceeds maximumClose connection (no response)
Unknown API keyClose connection (invalid request)
Unsupported API versionUNSUPPORTED_VERSION error
Truncated messageClose connection
Invalid encodingClose connection

Brokers must close connections without response for:

  • Size field exceeds socket.request.max.bytes
  • Incomplete frame (connection closed mid-message)
  • Malformed header preventing response construction

RequirementLevel
Generate unique correlation IDs per connectionmust
Track pending requests with timeoutmust
Handle out-of-order response (should not occur)should
Support pipeliningshould
Respect max.in.flight configurationmust
RequirementLevel
Validate Size field before allocationmust
Process requests in ordermust
Send responses in request ordermust
Echo correlation_id exactlymust
Support ApiVersions pre-authenticationmust