apache/pulsar · error · IllegalArgumentException
Must set timeout unit for timeout.
Error message
Must set timeout unit for timeout.
What it means
BatchReceivePolicy.verify() additionally requires that if timeout is set (> 0), a timeoutUnit must also be provided; a timeout without a time unit is ambiguous and rejected with IllegalArgumentException. Internally getTimeoutMs() also silently yields 0 without a unit, so the check prevents a misleading no-timeout policy.
Source
Thrown at pulsar-client-api/src/main/java/org/apache/pulsar/client/api/BatchReceivePolicy.java:104
* 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;
}
public boolean isMessagesFromMultiTopicsEnabled() {
return messagesFromMultiTopicsEnabled;
}View on GitHub (pinned to 820761864e)
Solutions
- Use the builder's timeout(long, TimeUnit) convenience so unit and value are set together.
- If setting fields manually, also set timeoutUnit (e.g. TimeUnit.MILLISECONDS).
- Set timeout <= 0 if you truly want no timeout, so the check is skipped.
Example fix
// before
BatchReceivePolicy policy = BatchReceivePolicy.builder()
.maxNumMessages(10)
.timeout(100)
.build();
// after
BatchReceivePolicy policy = BatchReceivePolicy.builder()
.maxNumMessages(10)
.timeout(100, TimeUnit.MILLISECONDS)
.build(); Defensive patterns
Strategy: validation
Validate before calling
if (policy.getTimeout() > 0 && policy.getTimeoutUnit() == null) {
throw new IllegalArgumentException("timeout set without timeoutUnit");
}
policy.verify(); Try / catch
try {
policy.verify();
} catch (IllegalArgumentException e) {
log.error("BatchReceivePolicy misconfigured: {}", e.getMessage());
} Prevention
- Always use the builder's timeout(long, TimeUnit) overload so the unit is never omitted.
- Never set the timeout field directly without also setting timeoutUnit.
When it happens
Trigger: Building a BatchReceivePolicy with timeout(N) (or a raw timeout field > 0) but never calling timeoutUnit/timeout(long, TimeUnit), then passing it to ConsumerBuilder.batchReceivePolicy().
Common situations: Using the builder's timeout(int) or setting fields directly on the policy without specifying the TimeUnit; deserializing policy from config where the unit field was omitted.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- 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
- At least one of maxNumMessages, maxNumBytes, timeout must be
- Broker doesn't allow forced deletion of namespaces
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/e3d2e8a11e6d878b.
Report an issue: GitHub.