alibaba/spring-ai-alibaba · error · IllegalArgumentException

maxAttempts must be greater than or equal to 1

Error message

maxAttempts must be greater than or equal to 1

What it means

ModelRetryInterceptor.Builder.maxAttempts(int) validates that the maximum attempt count is at least 1 (the count includes the first call). Passing 0 or a negative value throws IllegalArgumentException at builder time, before any model call happens.

Source

Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/modelretry/ModelRetryInterceptor.java:300

	@Override
	public String getName() {
		return "ModelRetry";
	}

	public static class Builder {
		private int maxAttempts = 3;
		private long initialDelay = 1000;
		private long maxDelay = 30000;
		private double backoffMultiplier = 2.0;
		private Predicate<Exception> retryableExceptionPredicate = Builder::isRetryableException;

		/**
		 * Set the maximum number of retries (including the first call).
		 * @param maxAttempts The maximum number of attempts must be >= 1.
		 */
		public Builder maxAttempts(int maxAttempts) {
			if (maxAttempts < 1) {
				throw new IllegalArgumentException("maxAttempts must be greater than or equal to 1");
			}
			this.maxAttempts = maxAttempts;
			return this;
		}

		/**
		 * Set the initial retry delay (milliseconds).
		 * @param initialDelay Initial delay time, in milliseconds
		 */
		public Builder initialDelay(long initialDelay) {
			if (initialDelay < 0) {
				throw new IllegalArgumentException("initialDelay must be greater than or equal to 0.");
			}
			this.initialDelay = initialDelay;
			return this;
		}

		/**

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Pass a value >= 1; remember 1 means the initial call with no retries, 3 means up to 2 retries.
  2. Clamp or validate configuration at startup: Math.max(1, configuredMaxAttempts).
  3. If you want retries disabled entirely, use maxAttempts(1) rather than 0.

Example fix

// before
int attempts = Integer.parseInt(props.getProperty("retry.maxAttempts", "0"));
ModelRetryInterceptor.builder().maxAttempts(attempts).build();
// after
int attempts = Math.max(1, Integer.parseInt(props.getProperty("retry.maxAttempts", "3")));
ModelRetryInterceptor.builder().maxAttempts(attempts).build();
Defensive patterns

Strategy: validation

Validate before calling

public static ModelRetryInterceptor buildRetry(int maxAttempts) {
    if (maxAttempts < 1) throw new IllegalArgumentException("maxAttempts must be >= 1 (1 = no retries)");
    return ModelRetryInterceptor.builder().maxAttempts(maxAttempts).build();
}

Try / catch

try {
    builder.maxAttempts(cfg.maxAttempts());
} catch (IllegalArgumentException e) {
    log.warn("Invalid maxAttempts in config ({}), defaulting to 3", cfg.maxAttempts());
    builder.maxAttempts(3);
}

Prevention

When it happens

Trigger: Calling ModelRetryInterceptor.builder().maxAttempts(0) or maxAttempts(negative) — commonly from an external config value (properties file, env var) that is unset, 0, or mis-parsed.

Common situations: Binding maxAttempts from application.properties/yaml where a 'retries: 0' convention was assumed; integer parse failures defaulting to 0; arithmetic producing a negative value; thinking the setting means 'extra retries' rather than total attempts.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/cde343dc395f2de2. Report an issue: GitHub.