{"id":"6b8f01ec34a3ad93","repo":"apache/kafka","slug":"invalidconfigs-cannot-be-set-when-group-protocol","errorCode":null,"errorMessage":"{invalidConfigs} cannot be set when group.protocol={groupProtocol}","messagePattern":"(.+?) cannot be set when group\\.protocol=(.+?)","errorType":"exception","errorClass":"ConfigException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/consumer/ConsumerConfig.java","lineNumber":838,"sourceCode":"        String groupProtocol = getString(GROUP_PROTOCOL_CONFIG);\n        if (GroupProtocol.CLASSIC.name().equalsIgnoreCase(groupProtocol)) {\n            checkUnsupportedConfigsPostProcess(GroupProtocol.CLASSIC, CLASSIC_PROTOCOL_UNSUPPORTED_CONFIGS);\n        } else if (GroupProtocol.CONSUMER.name().equalsIgnoreCase(groupProtocol)) {\n            checkUnsupportedConfigsPostProcess(GroupProtocol.CONSUMER, CONSUMER_PROTOCOL_UNSUPPORTED_CONFIGS);\n        }\n    }\n\n    private void checkUnsupportedConfigsPostProcess(GroupProtocol groupProtocol, List<String> unsupportedConfigs) {\n        if (getString(GROUP_PROTOCOL_CONFIG).equalsIgnoreCase(groupProtocol.name())) {\n            List<String> invalidConfigs = new ArrayList<>();\n            unsupportedConfigs.forEach(configName -> {\n                Object config = originals().get(configName);\n                if (config != null && !Utils.isBlank(config.toString())) {\n                    invalidConfigs.add(configName);\n                }\n            });\n            if (!invalidConfigs.isEmpty()) {\n                throw new ConfigException(String.join(\", \", invalidConfigs) +\n                        \" cannot be set when \" + GROUP_PROTOCOL_CONFIG + \"=\" + groupProtocol.name());\n            }\n        }\n    }\n\n    /**\n     * Constructs a new ConsumerConfig with the given properties.\n     *\n     * @param props The consumer configuration properties\n     */\n    public ConsumerConfig(Properties props) {\n        super(CONFIG, props);\n    }\n\n    /**\n     * Constructs a new ConsumerConfig with the given properties.\n     *\n     * @param props The consumer configuration properties","sourceCodeStart":820,"sourceCodeEnd":856,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/ConsumerConfig.java#L820-L856","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (group.protocol=consumer with classic configs)\nprops.put(ConsumerConfig.GROUP_PROTOCOL_CONFIG, \"consumer\");\nprops.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG, CooperativeStickyAssignor.class.getName());\nprops.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, \"3000\");\n// throws: partition.assignment.strategy, heartbeat.interval.ms cannot be set when group.protocol=CONSUMER\n\n// after (pick one)\n// Option A: stay on consumer protocol, drop classic configs\nprops.remove(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG);\nprops.remove(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG);\n\n// Option B: keep classic tuning, use classic protocol\nprops.put(ConsumerConfig.GROUP_PROTOCOL_CONFIG, \"classic\");","handlingStrategy":"validation","validationCode":"// Reject mutually-exclusive configs up front based on group.protocol:\nString protocol = (String) props.get(ConsumerConfig.GROUP_PROTOCOL_CONFIG);\nList<String> classicUnsupported = List.of(/* CLASSIC_PROTOCOL_UNSUPPORTED_CONFIGS entries, e.g. remote.assignor */);\nList<String> consumerUnsupported = List.of(/* CONSUMER_PROTOCOL_UNSUPPORTED_CONFIGS entries */);\nList<String> unsupported = GroupProtocol.CONSUMER.name().equalsIgnoreCase(protocol) ? consumerUnsupported\n                         : GroupProtocol.CLASSIC.name().equalsIgnoreCase(protocol) ? classicUnsupported\n                         : Collections.emptyList();\nList<String> present = unsupported.stream()\n    .filter(c -> props.get(c) != null && !props.get(c).toString().isBlank())\n    .toList();\nif (!present.isEmpty()) {\n    throw new ConfigException(present + \" cannot be set when group.protocol=\" + protocol);\n}","typeGuard":null,"tryCatchPattern":"try {\n    new KafkaConsumer<>(props);\n} catch (ConfigException e) {\n    if (e.getMessage().contains(\"cannot be set when group.protocol=\")) {\n        // strip the offending keys and retry, or fail fast with a clear app-level message\n        props.keySet().removeIf(k -> e.getMessage().contains(k));\n        new KafkaConsumer<>(props);\n    } else throw e;\n}","preventionTips":["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."],"tags":["consumer","configuration","group-protocol","kip-848","startup","java"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}