#1432·kafka-go

ConsumerGroup.joinGroup doesn't handle MemberIdRequired (KIP-394)

Author: erikcwCreated Apr 24, 2026Updated Jul 9, 2026

Summary

The ConsumerGroup implementation doesn't handle MemberIdRequired (error code 79) per KIP-394. When a broker returns this error with an assigned member ID in the response, the client should retry the JoinGroup request using that member ID. Instead, the current code treats it as a fatal error, discards the member ID, and retries with an empty string, creating an infinite failure loop.

Root cause

In consumergroup.go, the joinGroup method (line ~941) treats any non-zero error code as fatal:

go
if err == nil && response.ErrorCode != 0 {
    err = Error(response.ErrorCode)
}
if err != nil {
    return "", 0, nil, err  // member ID from response is lost
}

The caller nextGeneration then hits the default case in the error switch, which clears the member ID and retries after a backoff:

go
default:
    _ = cg.leaveGroup(memberID)
    memberID = ""
    backoff = time.After(cg.config.JoinGroupBackoff)

The member ID returned in the MemberIdRequired response is never preserved.

Expected behavior

Per KIP-394, when the broker returns MemberIdRequired:

  1. Extract the MemberID from the JoinGroup response
  2. Retry the JoinGroup request with that member ID
  3. The second attempt should succeed

How to reproduce

Connect a ConsumerGroup to a Kafka-compatible broker that enforces KIP-394 (e.g., Tansu). The consumer group will fail to join and retry indefinitely with [79] Member ID Required.

Suggested fix

In joinGroup, handle MemberIdRequired by extracting the member ID from the response and retrying:

go
response, err := conn.joinGroup(request)
if err == nil && response.ErrorCode != 0 {
    if Error(response.ErrorCode) == MemberIDRequired && response.MemberID != "" {
        request.MemberID = response.MemberID
        response, err = conn.joinGroup(request)
        if err == nil && response.ErrorCode != 0 {
            err = Error(response.ErrorCode)
        }
    } else {
        err = Error(response.ErrorCode)
    }
}

Alternatively, handle it in the nextGeneration error switch by not clearing the member ID for this specific error.

References