apache/kafka · error · IllegalStateException

Offsets from multiple consumer groups were requested. Use pa

Error message

Offsets from multiple consumer groups were requested. Use partitionsToOffsetAndMetadata(groupId) instead to get future for a specific group.

What it means

Thrown by ListConsumerGroupOffsetsResult.partitionsToOffsetAndMetadata() (the no-arg overload) when the underlying result carries futures for more than one consumer group. The no-arg accessor is only valid when exactly one group was queried; when listConsumerGroupOffsets was called with a Map of multiple group keys, you must disambiguate by passing the specific groupId to the partitionsToOffsetAndMetadata(String) overload (or call all() for the full multi-group result).

Source

Thrown at clients/src/main/java/org/apache/kafka/clients/admin/ListConsumerGroupOffsetsResult.java:53

 * <p>
 */
@InterfaceAudience.Public
public class ListConsumerGroupOffsetsResult {

    final Map<String, KafkaFuture<Map<TopicPartition, OffsetAndMetadata>>> futures;

    ListConsumerGroupOffsetsResult(final Map<CoordinatorKey, KafkaFuture<Map<TopicPartition, OffsetAndMetadata>>> futures) {
        this.futures = futures.entrySet().stream()
                .collect(Collectors.toMap(e -> e.getKey().idValue, Entry::getValue));
    }

    /**
     * Return a future which yields a map of topic partitions to OffsetAndMetadata objects.
     * If the group does not have a committed offset for this partition, the corresponding value in the returned map will be null.
     */
    public KafkaFuture<Map<TopicPartition, OffsetAndMetadata>> partitionsToOffsetAndMetadata() {
        if (futures.size() != 1) {
            throw new IllegalStateException("Offsets from multiple consumer groups were requested. " +
                    "Use partitionsToOffsetAndMetadata(groupId) instead to get future for a specific group.");
        }
        return futures.values().iterator().next();
    }

    /**
     * Return a future which yields a map of topic partitions to OffsetAndMetadata objects for
     * the specified group. If the group doesn't have a committed offset for a specific
     * partition, the corresponding value in the returned map will be null.
     */
    public KafkaFuture<Map<TopicPartition, OffsetAndMetadata>> partitionsToOffsetAndMetadata(String groupId) {
        if (!futures.containsKey(groupId))
            throw new IllegalArgumentException("Offsets for consumer group '" + groupId + "' were not requested.");
        return futures.get(groupId);
    }

    /**
     * Return a future which yields all {@code Map<String, Map<TopicPartition, OffsetAndMetadata>} objects,

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Call partitionsToOffsetAndMetadata(groupId) with the specific group you want from the multi-group result.
  2. Or call all() to obtain a future yielding Map<String, Map<TopicPartition, OffsetAndMetadata>> for every requested group.
  3. If the intent truly is a single group, call listConsumerGroupOffsets(singleGroupId) (or a one-entry map) so the no-arg accessor remains valid.

Example fix

// before
ListConsumerGroupOffsetsResult result =
    admin.listConsumerGroupOffsets(Map.of("g1", Set.of(tp), "g2", Set.of(tp)));
Map<TopicPartition, OffsetAndMetadata> offsets =
    result.partitionsToOffsetAndMetadata().get(); // IllegalStateException

// after - pick a specific group
Map<TopicPartition, OffsetAndMetadata> g1Offsets =
    result.partitionsToOffsetAndMetadata("g1").get();
// or get every group at once
Map<String, Map<TopicPartition, OffsetAndMetadata>> all =
    result.all().get();
Defensive patterns

Strategy: validation

Validate before calling

ListConsumerGroupOffsetsResult result = admin.listConsumerGroupOffsets(requestedGroups);
if (requestedGroups.size() != 1) {
    // multi-group: use all() or partitionsToOffsetAndMetadata(groupId) per group
    Map<String, Map<TopicPartition, OffsetAndMetadata>> all = result.all().get();
} else {
    Map<TopicPartition, OffsetAndMetadata> single =
        result.partitionsToOffsetAndMetadata(requestedGroups.iterator().next()).get();
}

Try / catch

if (result.futures().size() == 1) {
    result.partitionsToOffsetAndMetadata(); // safe
} else {
    // multiple groups requested -> call partitionsToOffsetAndMetadata(groupId) or all()
    result.all().get();
}

Prevention

When it happens

Trigger: Calling admin.listConsumerGroupOffsets(Map.of(groupA, partitions, groupB, partitions)) and then invoking .partitionsToOffsetAndMetadata() on the result without a groupId; the futures map has size 2, so the no-arg accessor throws IllegalStateException.

Common situations: Refactoring single-group offset tooling to a multi-group batch query without switching the accessor overload; generic helper that always calls the no-arg partitionsToOffsetAndMetadata regardless of how many groups were requested; copying example code that assumed a single group.

Related errors


AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03). Data as JSON: /data/errors/85f84623b101b836.json. Report an issue: GitHub.