apache/kafka · error · ConfigException
{invalidConfigs} cannot be set when group.protocol={groupPro
Error message
{invalidConfigs} cannot be set when group.protocol={groupProtocol} What it means
Thrown by ConsumerConfig.checkUnsupportedConfigsPostProcess when one or more config keys incompatible with the selected group.protocol are present. For CLASSIC protocol the forbidden keys are group.remote.assignor, share.acknowledgement.mode, share.acquire.mode; for CONSUMER (the new KIP-848 protocol) the forbidden keys also include partition.assignment.strategy, heartbeat.interval.ms, and session.timeout.ms (these are owned by the broker in the new protocol). It is a ConfigException raised at consumer construction.
Source
Thrown at clients/src/main/java/org/apache/kafka/clients/consumer/ConsumerConfig.java:838
String groupProtocol = getString(GROUP_PROTOCOL_CONFIG);
if (GroupProtocol.CLASSIC.name().equalsIgnoreCase(groupProtocol)) {
checkUnsupportedConfigsPostProcess(GroupProtocol.CLASSIC, CLASSIC_PROTOCOL_UNSUPPORTED_CONFIGS);
} else if (GroupProtocol.CONSUMER.name().equalsIgnoreCase(groupProtocol)) {
checkUnsupportedConfigsPostProcess(GroupProtocol.CONSUMER, CONSUMER_PROTOCOL_UNSUPPORTED_CONFIGS);
}
}
private void checkUnsupportedConfigsPostProcess(GroupProtocol groupProtocol, List<String> unsupportedConfigs) {
if (getString(GROUP_PROTOCOL_CONFIG).equalsIgnoreCase(groupProtocol.name())) {
List<String> invalidConfigs = new ArrayList<>();
unsupportedConfigs.forEach(configName -> {
Object config = originals().get(configName);
if (config != null && !Utils.isBlank(config.toString())) {
invalidConfigs.add(configName);
}
});
if (!invalidConfigs.isEmpty()) {
throw new ConfigException(String.join(", ", invalidConfigs) +
" cannot be set when " + GROUP_PROTOCOL_CONFIG + "=" + groupProtocol.name());
}
}
}
/**
* Constructs a new ConsumerConfig with the given properties.
*
* @param props The consumer configuration properties
*/
public ConsumerConfig(Properties props) {
super(CONFIG, props);
}
/**
* Constructs a new ConsumerConfig with the given properties.
*
* @param props The consumer configuration propertiesView on GitHub (pinned to c31c9215e1)
Solutions
- Remove the listed invalidConfigs from your consumer properties for the chosen group.protocol.
- If using group.protocol=consumer, drop partition.assignment.strategy, heartbeat.interval.ms, and session.timeout.ms (the broker manages them now).
- If you need classic-era tuning knobs (custom assignor, specific heartbeat/session timeouts), keep group.protocol=classic and remove the consumer-protocol-only configs.
- Remove share.acknowledgement.mode and share.acquire.mode unless you are constructing a KafkaShareConsumer.
Example fix
// before (group.protocol=consumer with classic configs) props.put(ConsumerConfig.GROUP_PROTOCOL_CONFIG, "consumer"); props.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG, CooperativeStickyAssignor.class.getName()); props.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, "3000"); // throws: partition.assignment.strategy, heartbeat.interval.ms cannot be set when group.protocol=CONSUMER // after (pick one) // Option A: stay on consumer protocol, drop classic configs props.remove(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG); props.remove(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG); // Option B: keep classic tuning, use classic protocol props.put(ConsumerConfig.GROUP_PROTOCOL_CONFIG, "classic");
Defensive patterns
Strategy: validation
Validate before calling
// Reject mutually-exclusive configs up front based on group.protocol:
String protocol = (String) props.get(ConsumerConfig.GROUP_PROTOCOL_CONFIG);
List<String> classicUnsupported = List.of(/* CLASSIC_PROTOCOL_UNSUPPORTED_CONFIGS entries, e.g. remote.assignor */);
List<String> consumerUnsupported = List.of(/* CONSUMER_PROTOCOL_UNSUPPORTED_CONFIGS entries */);
List<String> unsupported = GroupProtocol.CONSUMER.name().equalsIgnoreCase(protocol) ? consumerUnsupported
: GroupProtocol.CLASSIC.name().equalsIgnoreCase(protocol) ? classicUnsupported
: Collections.emptyList();
List<String> present = unsupported.stream()
.filter(c -> props.get(c) != null && !props.get(c).toString().isBlank())
.toList();
if (!present.isEmpty()) {
throw new ConfigException(present + " cannot be set when group.protocol=" + protocol);
} Try / catch
try {
new KafkaConsumer<>(props);
} catch (ConfigException e) {
if (e.getMessage().contains("cannot be set when group.protocol=")) {
// strip the offending keys and retry, or fail fast with a clear app-level message
props.keySet().removeIf(k -> e.getMessage().contains(k));
new KafkaConsumer<>(props);
} else throw e;
} Prevention
- Check ConsumerConfig.CLASSIC_PROTOCOL_UNSUPPORTED_CONFIGS and CONSUMER_PROTOCOL_UNSUPPORTED_CONFIGS for the current list of mutually-exclusive configs.
- Pick group.protocol once per application and prune any incompatible configs in your config builder.
- When migrating from CLASSIC to CONSUMER protocol, audit partition.assignment.strategy and assignor-specific configs.
When it happens
Trigger: Constructing a KafkaConsumer with group.protocol=consumer while also setting partition.assignment.strategy, heartbeat.interval.ms, or session.timeout.ms; or setting group.protocol=classic while also setting group.remote.assignor; or including share-group configs (share.acknowledgement.mode, share.acquire.mode) on a non-share consumer.
Common situations: Migrating to the new consumer group protocol (KIP-848) without removing classic-era tuning knobs; reusing a large shared properties file across consumers with different protocols; copy-pasting tuning guides written for classic protocol onto a consumer.group.protocol=consumer config; accidentally setting share-consumer configs on a normal consumer.
Related errors
- Invalid value null for configuration key.deserializer: must
- Invalid value null for configuration value.deserializer: mus
- enable.auto.commit cannot be set to true when default group
- {klass} ClassNotFoundException exception occurred
- Subscribe to RE2/J pattern is not supported when usingthe %s
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/6b8f01ec34a3ad93.json.
Report an issue: GitHub.