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
- Set at least one of maxNumMessages, maxNumBytes, or timeout to a positive value in the builder.
- Use BatchReceivePolicy.DEFAULT_POLICY if you want sensible defaults.
- 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
- Always set at least one limit in the BatchReceivePolicy builder.
- Call verify() immediately after building to fail at configuration time.
- Use BatchReceivePolicy.DEFAULT_POLICY when in doubt.
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Invalid auto split/merge configuration: ${message}
- Cannot use receive() when a listener has been set
- Can't use receive with timeout, if the queue size is 0
- Can't use batch receive, if the queue size is 0
- Must set timeout unit for timeout.
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/d9af8561c09a3518.
Report an issue: GitHub.