apache/pulsar · error · IllegalArgumentException
timeout must not be negative
Error message
timeout must not be negative
What it means
ProcessingTimeoutPolicy rejects a negative Duration in its private constructor. A negative processing timeout is meaningless — it would represent a deadline already in the past — so the library fails fast with IllegalArgumentException instead of producing undefined redelivery behavior.
Source
Thrown at pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/ProcessingTimeoutPolicy.java:56
* when the application's processing time is bounded and you want stalled deliveries to
* be reattempted automatically.
*
* <p>Use {@link #of(Duration)} for the common case (no extra backoff), or
* {@link #builder()} to also configure {@code redeliveryBackoff}.
*/
@EqualsAndHashCode
@ToString
public final class ProcessingTimeoutPolicy {
private final Duration timeout;
private final BackoffPolicy redeliveryBackoff;
private ProcessingTimeoutPolicy(Duration timeout, BackoffPolicy redeliveryBackoff) {
if (timeout == null) {
throw new IllegalArgumentException("timeout must not be null");
}
if (timeout.isNegative()) {
throw new IllegalArgumentException("timeout must not be negative");
}
this.timeout = timeout;
this.redeliveryBackoff = redeliveryBackoff;
}
/**
* @return how long the client waits for the application to ack a delivery before
* requesting redelivery; {@link Duration#ZERO} disables
*/
public Duration timeout() {
return timeout;
}
/**
* @return optional backoff applied between redeliveries, or {@code null} for the
* default (no extra delay)
*/
public BackoffPolicy redeliveryBackoff() {View on GitHub (pinned to 820761864e)
Solutions
- Pass a non-negative Duration; use Duration.ZERO if 'no timeout' is intended (zero is explicitly allowed)
- Guard the value before building: if (d.isNegative()) throw new IllegalArgumentException(...) or clamp with a default
Example fix
// before Duration timeout = Duration.between(lastSeen, Instant.now()); // can be negative ProcessingTimeoutPolicy p = ProcessingTimeoutPolicy.builder().timeout(timeout).build(); // after Duration timeout = Duration.between(lastSeen, Instant.now()); if (timeout.isNegative()) timeout = Duration.ZERO; ProcessingTimeoutPolicy p = ProcessingTimeoutPolicy.builder().timeout(timeout).build();
Defensive patterns
Strategy: validation
Validate before calling
if (timeout != null && timeout.isNegative()) {
throw new IllegalArgumentException("processing timeout must be non-negative, got: " + timeout);
} Type guard
boolean isNonNegative(Duration d) { return d != null && !d.isNegative(); } Try / catch
try {
policy = ProcessingTimeoutPolicy.builder().timeout(timeout).build();
} catch (IllegalArgumentException e) {
log.warn("Negative timeout rejected, falling back to default");
policy = ProcessingTimeoutPolicy.builder().timeout(Duration.ofSeconds(30)).build();
} Prevention
- Never compute a Duration by subtracting timestamps without clamping to zero
- Reject or clamp negative values at the config-parsing layer
- Remember Duration.ZERO is allowed; negatives are not
When it happens
Trigger: Passing a negative Duration (e.g. Duration.ofSeconds(-1), Duration.ofMillis(negativeValue)) to ProcessingTimeoutPolicy.builder().timeout(...) or the of() factory.
Common situations: Arithmetic on Durations producing negative results (earlier time minus later time), or misconfigured config values parsed as negative numbers and wrapped in Duration.of*().
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- timeout must not be null
- at least one key name must be configured
- timeout must be > 0
- Timeout during delete operation
- Timeout during close operation
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/aadd75a7b9774046.
Report an issue: GitHub.