apache/pulsar · error · IllegalArgumentException
multiplier must be >= 1.0
Error message
multiplier must be >= 1.0
What it means
BackoffPolicy's private constructor validates its parameters: a multiplier strictly below 1.0 throws IllegalArgumentException('multiplier must be >= 1.0'). The multiplier is the exponential growth factor applied to each retry interval; a factor under 1.0 would shrink intervals every attempt, producing an ever-faster (and effectively infinite-rate) retry loop, so the builder rejects it up front.
Source
Thrown at pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/BackoffPolicy.java:52
* the common cases, or {@link #builder()} to configure all knobs explicitly.
*/
@EqualsAndHashCode
@ToString
public final class BackoffPolicy {
/** Default jitter percentage applied when not explicitly specified. */
public static final double DEFAULT_JITTER_PERCENT = 10.0;
private final Duration initialInterval;
private final Duration maxInterval;
private final double multiplier;
private final double jitterPercent;
private BackoffPolicy(Duration initialInterval, Duration maxInterval, double multiplier, double jitterPercent) {
Objects.requireNonNull(initialInterval, "initialInterval must not be null");
Objects.requireNonNull(maxInterval, "maxInterval must not be null");
if (multiplier < 1.0) {
throw new IllegalArgumentException("multiplier must be >= 1.0");
}
if (jitterPercent < 0 || jitterPercent > 100) {
throw new IllegalArgumentException("jitterPercent must be in [0, 100]");
}
this.initialInterval = initialInterval;
this.maxInterval = maxInterval;
this.multiplier = multiplier;
this.jitterPercent = jitterPercent;
}
/**
* @return the base delay before the first reconnection attempt
*/
public Duration initialInterval() {
return initialInterval;
}
/**View on GitHub (pinned to 820761864e)
Solutions
- Set the multiplier to 1.0 or greater — typical exponential backoff uses 2.0.
- Use multiplier 1.0 if you want constant-interval retries with only jitter variation.
- Clamp the configured value in your config layer: Math.max(1.0, configuredMultiplier) before handing it to the builder.
Example fix
// before
BackoffPolicy policy = BackoffPolicy.builder()
.initialInterval(Duration.ofMillis(100))
.maxInterval(Duration.ofSeconds(10))
.multiplier(0.5) // IllegalArgumentException
.build();
// after
BackoffPolicy policy = BackoffPolicy.builder()
.initialInterval(Duration.ofMillis(100))
.maxInterval(Duration.ofSeconds(10))
.multiplier(2.0)
.build(); Defensive patterns
Strategy: validation
Validate before calling
double multiplier = readConfig("retry.multiplier");
if (multiplier < 1.0)
throw new IllegalArgumentException("retry.multiplier must be >= 1.0, got " + multiplier);
BackoffPolicy policy = BackoffPolicy.builder().multiplier(multiplier).build(); Type guard
static boolean isValidMultiplier(double m) {
return m >= 1.0;
} Try / catch
try {
policy = BackoffPolicy.builder()
.initialInterval(init).maxInterval(max).multiplier(cfg.multiplier()).build();
} catch (IllegalArgumentException e) {
if (e.getMessage().equals("multiplier must be >= 1.0")) {
policy = BackoffPolicy.builder()
.initialInterval(init).maxInterval(max).multiplier(2.0).build(); // safe default
} else throw e;
} Prevention
- Clamp configured multipliers with Math.max(1.0, value) at config load time.
- Use 2.0 as the standard exponential factor; use 1.0 for constant-interval retries.
- Add a validation unit test covering the boundary values 0.99 (rejected) and 1.0 (accepted).
When it happens
Trigger: Calling BackoffPolicy builder/withMultiplier(0.5) (or any value < 1.0) before the policy is constructed — the private constructor runs on build(), so the exception surfaces there.
Common situations: Confusing multiplier semantics (expecting a decay factor like 0.5 to 'slow down'); porting jitter/backoff constants from another library where the factor was expressed differently; typos such as 0.1 intended as a 10% growth written as an absolute value.
Understand the failure class
Background: "Invalid configuration value" and "Unsupported/Unknown setting value" errors: why libraries reject your config strings, numbers, and types — this error's family across 30 libraries.
Related errors
- jitterPercent must be in [0, 100]
- jitterPercent must be in [0, 100]
- configuredService should not be an instance of SystemTopicBa
- Invalid auto split/merge configuration: ${message}
- reconsumeLater method not supported because retryEnabled is
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/3a44e8367d4995d2.
Report an issue: GitHub.