apache/pulsar · error · IllegalArgumentException

At least one of maxNumMessages, maxNumBytes, timeout must be

Error message

At least one of maxNumMessages, maxNumBytes, timeout must be specified.

What it means

BatchReceivePolicy.verify() enforces that a batch receive policy is not a no-op: at least one bound among maxNumMessages, maxNumBytes, and timeout must be positive, otherwise the consumer could wait forever without any limit. If all three are <= 0, it throws IllegalArgumentException. This runs when the policy is attached to a ConsumerBuilder.

Source

Thrown at pulsar-client-api/src/main/java/org/apache/pulsar/client/api/BatchReceivePolicy.java:100

     */
    private final int maxNumBytes;

    /**
     * timeout for waiting for enough messages(enough number or enough bytes).
     */
    private final int timeout;
    private final TimeUnit timeoutUnit;


    /**
     * If it is false, one time `batchReceive()` only can receive the single topic messages,
     * the max messages and max size will not be strictly followed. (default: true).
     */
    private final boolean messagesFromMultiTopicsEnabled;

    public void verify() {
        if (maxNumMessages <= 0 && maxNumBytes <= 0 && timeout <= 0) {
            throw new IllegalArgumentException("At least "
                    + "one of maxNumMessages, maxNumBytes, timeout must be specified.");
        }
        if (timeout > 0 && timeoutUnit == null) {
            throw new IllegalArgumentException("Must set timeout unit for timeout.");
        }
    }

    public long getTimeoutMs() {
        return (timeout > 0 && timeoutUnit != null) ? timeoutUnit.toMillis(timeout) : 0L;
    }

    public int getMaxNumMessages() {
        return maxNumMessages;
    }

    public int getMaxNumBytes() {
        return maxNumBytes;
    }

View on GitHub (pinned to 820761864e)

Solutions

  1. Set at least one of maxNumMessages, maxNumBytes, or timeout to a positive value in the builder.
  2. Use BatchReceivePolicy.DEFAULT_POLICY if you want sensible defaults.
  3. Call policy.verify() yourself (try/catch IllegalArgumentException) right after building to fail fast at configuration time.

Example fix

// before
BatchReceivePolicy policy = BatchReceivePolicy.builder().build();
// after
BatchReceivePolicy policy = BatchReceivePolicy.builder()
    .maxNumMessages(100)
    .maxNumBytes(10 * 1024 * 1024)
    .timeout(100, TimeUnit.MILLISECONDS)
    .build();
Defensive patterns

Strategy: validation

Validate before calling

BatchReceivePolicy policy = /* built policy */;
boolean bounded = policy.getMaxNumMessages() > 0
    || policy.getMaxNumBytes() > 0
    || policy.getTimeout() > 0;
if (!bounded) {
    throw new IllegalArgumentException(
        "BatchReceivePolicy needs a positive maxNumMessages, maxNumBytes or timeout");
}
policy.verify();

Try / catch

try {
    policy.verify();
    consumerBuilder.batchReceivePolicy(policy);
} catch (IllegalArgumentException e) {
    log.error("Invalid BatchReceivePolicy: {}", e.getMessage());
    consumerBuilder.batchReceivePolicy(BatchReceivePolicy.DEFAULT_POLICY);
}

Prevention

When it happens

Trigger: ConsumerBuilder.batchReceivePolicy(BatchReceivePolicy.builder()...build()) where all limits were left at defaults 0/-1 (e.g. builder with no values), then consumerBuilder.subscribe() is called.

Common situations: Programmatically constructing BatchReceivePolicy from config where all values were absent and defaulted to -1/0; accidentally using the default constructor instead of DEFAULT_POLICY; building a policy but forgetting to set any field.

Understand the failure class

Related errors


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