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.

Solutions

  1. Use the builder's timeout(long, TimeUnit) convenience so unit and value are set together.
  2. If setting fields manually, also set timeoutUnit (e.g. TimeUnit.MILLISECONDS).
  3. 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

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

Related errors


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

Appendix: 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)