alibaba/spring-ai-alibaba · error · IllegalArgumentException

At least one limit must be specified (threadLimit or runLimi

Error message

At least one limit must be specified (threadLimit or runLimit)

What it means

ToolCallLimitHook.Builder.build() requires at least one of threadLimit or runLimit to be set. A hook with no limits would never restrict anything, so the builder rejects it as a configuration mistake.

Source

Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/hook/toolcalllimit/ToolCallLimitHook.java:216

		public Builder threadLimit(Integer threadLimit) {
			this.threadLimit = threadLimit;
			return this;
		}

		public Builder runLimit(Integer runLimit) {
			this.runLimit = runLimit;
			return this;
		}

		public Builder exitBehavior(ExitBehavior exitBehavior) {
			this.exitBehavior = exitBehavior;
			return this;
		}

		public ToolCallLimitHook build() {
			if (threadLimit == null && runLimit == null) {
				throw new IllegalArgumentException("At least one limit must be specified (threadLimit or runLimit)");
			}
			return new ToolCallLimitHook(this);
		}
	}
}

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Set at least one limit: .threadLimit(n) and/or .runLimit(m) before build().
  2. If you want only per-conversation caps use threadLimit; for per-run caps use runLimit; set both for combined enforcement.
  3. Skip constructing the hook entirely when no limiting is desired, instead of building one with no limits.

Example fix

// before
ToolCallLimitHook hook = ToolCallLimitHook.builder().build();
// after
ToolCallLimitHook hook = ToolCallLimitHook.builder().threadLimit(10).build();
Defensive patterns

Strategy: validation

Validate before calling

ToolCallLimitHook.Builder b = ToolCallLimitHook.builder();
// ensure at least one limit is set before build
if (threadLimit == null && runLimit == null) { throw new IllegalStateException("Set threadLimit or runLimit"); }
ToolCallLimitHook hook = b.threadLimit(threadLimit).build();

Type guard

boolean hasLimit(Integer threadLimit, Integer runLimit) { return threadLimit != null || runLimit != null; }

Try / catch

try { hook = builder.build(); } catch (IllegalArgumentException e) { log.error("ToolCallLimitHook misconfigured: {}", e.getMessage()); }

Prevention

When it happens

Trigger: Calling ToolCallLimitHook.builder().build() (or with only toolName/exitBehavior set) without ever calling .threadLimit(...) or .runLimit(...).

Common situations: Builder chain built dynamically where limit setters were skipped; copying a snippet and deleting the limit lines; assuming defaults exist (there are none).

Understand the failure class

Background: "Must pass :limit option" / "Missing required option" — required option errors explained — this error's family across 41 libraries.

Related errors


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