apache/pulsar · error · IllegalArgumentException

numPartitionsLimit should be greater than or equal to 1

Error message

numPartitionsLimit should be greater than or equal to 1

What it means

PartialRoundRobinMessageRouterImpl limits the number of partitions a producer will send to (a partial round-robin over partitions). The constructor validates numPartitionsLimit and throws this IllegalArgumentException for values below 1.

Source

Thrown at pulsar-client/src/main/java/org/apache/pulsar/client/impl/customroute/PartialRoundRobinMessageRouterImpl.java:43

import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.pulsar.client.api.Message;
import org.apache.pulsar.client.api.MessageRouter;
import org.apache.pulsar.client.api.TopicMetadata;

public class PartialRoundRobinMessageRouterImpl implements MessageRouter {
    private final int numPartitionsLimit;
    private final List<Integer> partialList = new CopyOnWriteArrayList<>();
    private static final AtomicIntegerFieldUpdater<PartialRoundRobinMessageRouterImpl> PARTITION_INDEX_UPDATER =
            AtomicIntegerFieldUpdater.newUpdater(PartialRoundRobinMessageRouterImpl.class, "partitionIndex");
    @SuppressWarnings("unused")
    private volatile int partitionIndex = 0;

    public PartialRoundRobinMessageRouterImpl(final int numPartitionsLimit) {
        if (numPartitionsLimit < 1) {
            throw new IllegalArgumentException("numPartitionsLimit should be greater than or equal to 1");
        }
        this.numPartitionsLimit = numPartitionsLimit;
    }

    /**
     * Choose a partition based on the topic metadata.
     * Key hash routing isn't supported.
     *
     * @param msg message
     * @param metadata topic metadata
     * @return the partition to route the message.
     */
    public int choosePartition(Message<?> msg, TopicMetadata metadata) {
        final List<Integer> newPartialList = new ArrayList<>(getOrCreatePartialList(metadata));
        return newPartialList
                .get(signSafeMod(PARTITION_INDEX_UPDATER.getAndIncrement(this), newPartialList.size()));
    }

View on GitHub (pinned to 820761864e)

Solutions

  1. Pass a numPartitionsLimit of at least 1.
  2. Clamp the configured value: Math.max(1, configuredLimit) before constructing the router.
  3. If the limit comes from configuration, validate it at load time with a clear error message.

Example fix

// before
int limit = Integer.parseInt(props.getProperty("numPartitionsLimit", "0"));
MessageRouter router = new PartialRoundRobinMessageRouterImpl(limit);
// after
int limit = Math.max(1, Integer.parseInt(props.getProperty("numPartitionsLimit", "1")));
MessageRouter router = new PartialRoundRobinMessageRouterImpl(limit);
Defensive patterns

Strategy: validation

Validate before calling

int limit = Integer.parseInt(props.getProperty("numPartitionsLimit", "1"));
if (limit < 1) throw new IllegalArgumentException("numPartitionsLimit must be >= 1, got " + limit);

Type guard

boolean isValidPartitionLimit(int n) { return n >= 1; }

Try / catch

try { router = new PartialRoundRobinMessageRouterImpl(limit); } catch (IllegalArgumentException e) { router = new PartialRoundRobinMessageRouterImpl(1); }

Prevention

When it happens

Trigger: Calling new PartialRoundRobinMessageRouterImpl(0) or any negative limit, or producerBuilder.partialRoundRobin(...) style APIs (e.g. ProducerBuilder#messageRouter or usePartialRoundRobin) with a limit configured as 0 or negative.

Common situations: Config value read from properties/default 0 before validation; off-by-one in code computing the limit from a partition count that is 0; misuse of the router constructor directly in unit tests.

Understand the failure class

Background: "must be a positive integer", "cannot be empty", "invalid argument": how invalid-argument errors work across open-source libraries — this error's family across 33 libraries.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/fe8a239d962bee82. Report an issue: GitHub.