apache/pulsar · error · IllegalArgumentException

maxRedeliverCount must be >= 0

Error message

maxRedeliverCount must be >= 0

What it means

DeadLetterPolicy requires maxRedeliverCount >= 0 because it defines how many redelivery attempts occur before a message is routed to the dead-letter topic; 0 means immediate dead-lettering and negative values have no meaning. The constructor throws IllegalArgumentException for negative values.

Source

Thrown at pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/DeadLetterPolicy.java:44

 *
 * <p>When a message has been redelivered more than {@code maxRedeliverCount} times,
 * it is moved to the dead letter topic instead of being redelivered again.
 *
 * <p>Construct via {@link #builder()}.
 */
@EqualsAndHashCode
@ToString
public final class DeadLetterPolicy {

    private final int maxRedeliverCount;
    private final String retryLetterTopic;
    private final String deadLetterTopic;
    private final String initialSubscriptionName;

    private DeadLetterPolicy(int maxRedeliverCount, String retryLetterTopic,
                             String deadLetterTopic, String initialSubscriptionName) {
        if (maxRedeliverCount < 0) {
            throw new IllegalArgumentException("maxRedeliverCount must be >= 0");
        }
        this.maxRedeliverCount = maxRedeliverCount;
        this.retryLetterTopic = retryLetterTopic;
        this.deadLetterTopic = deadLetterTopic;
        this.initialSubscriptionName = initialSubscriptionName;
    }

    /**
     * @return the maximum number of redelivery attempts before sending to the dead letter topic
     */
    public int maxRedeliverCount() {
        return maxRedeliverCount;
    }

    /**
     * @return the custom retry letter topic, or {@code null} for the auto-generated default
     */
    public String retryLetterTopic() {

View on GitHub (pinned to 820761864e)

Solutions

  1. Set maxRedeliverCount to 0 or a positive integer (e.g. 3 for three redeliveries before DLQ).
  2. If you meant 'unlimited redeliveries', do not use a negative number - omit dead-letter routing or use a large positive cap.
  3. Clamp or reject negative config values at load time: Math.max(0, configuredCount).

Example fix

// before
DeadLetterPolicy dlp = DeadLetterPolicy.builder()
    .maxRedeliverCount(-1) // IllegalArgumentException
    .build();

// after
DeadLetterPolicy dlp = DeadLetterPolicy.builder()
    .maxRedeliverCount(3)
    .build();
Defensive patterns

Strategy: validation

Validate before calling

int count = cfg.maxRedeliverCount();
if (count < 0) {
    throw new IllegalArgumentException("maxRedeliverCount must be >= 0, got: " + count);
}
DeadLetterPolicy dlp = DeadLetterPolicy.builder().maxRedeliverCount(count).build();

Type guard

static boolean isValidMaxRedeliverCount(int v) { return v >= 0; }

Try / catch

try {
    dlp = DeadLetterPolicy.builder().maxRedeliverCount(cfg).build();
} catch (IllegalArgumentException e) {
    log.warn("Invalid maxRedeliverCount, using default DLQ policy", e);
    dlp = DeadLetterPolicy.builder().build();
}

Prevention

When it happens

Trigger: Calling DeadLetterPolicy.builder().maxRedeliverCount(-1) or passing a negative value computed from config; sentinel -1 used for 'unlimited' or 'unset'.

Common situations: Using -1 to mean 'never dead-letter' (unsupported semantics here); config parsing an empty string into a negative default; arithmetic on retry counters that can go negative.

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


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