{"record":{"id":"fe8a239d962bee82","repo":"apache/pulsar","slug":"numpartitionslimit-should-be-greater-than-or-equal","errorCode":null,"errorMessage":"numPartitionsLimit should be greater than or equal to 1","messagePattern":"numPartitionsLimit should be greater than or equal to 1","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-client/src/main/java/org/apache/pulsar/client/impl/customroute/PartialRoundRobinMessageRouterImpl.java","lineNumber":43,"sourceCode":"import java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.atomic.AtomicIntegerFieldUpdater;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\nimport org.apache.pulsar.client.api.Message;\nimport org.apache.pulsar.client.api.MessageRouter;\nimport org.apache.pulsar.client.api.TopicMetadata;\n\npublic class PartialRoundRobinMessageRouterImpl implements MessageRouter {\n    private final int numPartitionsLimit;\n    private final List<Integer> partialList = new CopyOnWriteArrayList<>();\n    private static final AtomicIntegerFieldUpdater<PartialRoundRobinMessageRouterImpl> PARTITION_INDEX_UPDATER =\n            AtomicIntegerFieldUpdater.newUpdater(PartialRoundRobinMessageRouterImpl.class, \"partitionIndex\");\n    @SuppressWarnings(\"unused\")\n    private volatile int partitionIndex = 0;\n\n    public PartialRoundRobinMessageRouterImpl(final int numPartitionsLimit) {\n        if (numPartitionsLimit < 1) {\n            throw new IllegalArgumentException(\"numPartitionsLimit should be greater than or equal to 1\");\n        }\n        this.numPartitionsLimit = numPartitionsLimit;\n    }\n\n    /**\n     * Choose a partition based on the topic metadata.\n     * Key hash routing isn't supported.\n     *\n     * @param msg message\n     * @param metadata topic metadata\n     * @return the partition to route the message.\n     */\n    public int choosePartition(Message<?> msg, TopicMetadata metadata) {\n        final List<Integer> newPartialList = new ArrayList<>(getOrCreatePartialList(metadata));\n        return newPartialList\n                .get(signSafeMod(PARTITION_INDEX_UPDATER.getAndIncrement(this), newPartialList.size()));\n    }\n","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-client/src/main/java/org/apache/pulsar/client/impl/customroute/PartialRoundRobinMessageRouterImpl.java#L25-L61","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass a numPartitionsLimit of at least 1.","Clamp the configured value: Math.max(1, configuredLimit) before constructing the router.","If the limit comes from configuration, validate it at load time with a clear error message."],"exampleFix":"// before\nint limit = Integer.parseInt(props.getProperty(\"numPartitionsLimit\", \"0\"));\nMessageRouter router = new PartialRoundRobinMessageRouterImpl(limit);\n// after\nint limit = Math.max(1, Integer.parseInt(props.getProperty(\"numPartitionsLimit\", \"1\")));\nMessageRouter router = new PartialRoundRobinMessageRouterImpl(limit);","handlingStrategy":"validation","validationCode":"int limit = Integer.parseInt(props.getProperty(\"numPartitionsLimit\", \"1\"));\nif (limit < 1) throw new IllegalArgumentException(\"numPartitionsLimit must be >= 1, got \" + limit);","typeGuard":"boolean isValidPartitionLimit(int n) { return n >= 1; }","tryCatchPattern":"try { router = new PartialRoundRobinMessageRouterImpl(limit); } catch (IllegalArgumentException e) { router = new PartialRoundRobinMessageRouterImpl(1); }","preventionTips":["Default the limit to >= 1 at config load time.","Clamp user/config-provided values with Math.max(1, value).","Never derive the limit from a possibly-zero partition count."],"tags":["producer","router","argument-validation","configuration"],"backgroundTag":"invalid-argument","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}