apache/seatunnel · error · IllegalArgumentException

Retry times must be greater than 0

Error message

Retry times must be greater than 0

What it means

RetryUtils.retryWithException validates that the caller-supplied RetryMaterial has non-negative retryTimes before attempting execution. A negative value is rejected with IllegalArgumentException 'Retry times must be greater than 0'. Note the check is < 0, so 0 passes validation (meaning a single attempt, no retries) despite the message wording.

Source

Thrown at seatunnel-common/src/main/java/org/apache/seatunnel/common/utils/RetryUtils.java:41

@Slf4j
public class RetryUtils {

    /**
     * Execute the given execution with retry
     *
     * @param execution execution to execute
     * @param retryMaterial retry material, defined the condition to retry
     * @param <T> result type
     * @return result of execution
     */
    public static <T> T retryWithException(
            Execution<T, Exception> execution, RetryMaterial retryMaterial) throws Exception {
        final RetryCondition<Exception> retryCondition = retryMaterial.getRetryCondition();
        final int retryTimes = retryMaterial.getRetryTimes();

        if (retryMaterial.getRetryTimes() < 0) {
            throw new IllegalArgumentException("Retry times must be greater than 0");
        }
        Exception lastException;
        int i = 0;
        do {
            i++;
            try {
                return execution.execute();
            } catch (Exception e) {
                lastException = e;
                if (retryCondition != null && !retryCondition.canRetry(e)) {
                    if (retryMaterial.shouldThrowException()) {
                        throw e;
                    }
                } else {
                    // Otherwise it is retriable and we should retry
                    String attemptMessage =
                            "Failed to execute due to {}. Retrying attempt ({}/{}) after backoff of {} ms";
                    if (retryMaterial.getSleepTimeMillis() > 0) {

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Set retryTimes to a positive integer (>=1) in the RetryMaterial builder or config
  2. If retries should be disabled, use retryTimes 0 rather than a negative value
  3. Validate/clamp the value when loading it from user configuration before constructing RetryMaterial
  4. If unlimited-ish behavior is needed, pass a large retry count with an appropriate retry backoff

Example fix

// before
RetryMaterial material = RetryMaterial.builder().retryTimes(-1).build();
// after
RetryMaterial material = RetryMaterial.builder().retryTimes(3).build();
Defensive patterns

Strategy: validation

Validate before calling

int retryTimes = config.getInt("retry-times");
if (retryTimes < 0) {
  throw new IllegalArgumentException("retry-times must be >= 0, got: " + retryTimes);
}

Try / catch

try {
  return RetryUtils.retryWithException(execution, material);
} catch (IllegalArgumentException e) {
  log.error("Invalid retry material: {}", e.getMessage());
  throw e;
}

Prevention

When it happens

Trigger: Constructing RetryMaterial (e.g. RetryMaterial.builder().retryTimes(-1)) or otherwise passing a negative retryTimes into retryWithException. Typically caused by misread configuration where a sentinel -1 was intended to mean 'infinite' or 'disabled'.

Common situations: Config file value parsed to -1 for retry count; user setting retry-times: -1 expecting unlimited retries; arithmetic producing a negative value from a subtraction on config values.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/3f51992ca399f5b5. Report an issue: GitHub.