alibaba/spring-ai-alibaba · error · ModelCallLimitExceededException

Model call limits exceeded: ${threadCount}/${threadLimit} th

Error message

Model call limits exceeded: ${threadCount}/${threadLimit} thread, ${runCount}/${runLimit} run

What it means

ModelCallLimitHook.beforeModel() tracks model call counts per thread and per run. When a configured limit is reached and exitBehavior is ExitBehavior.ERROR, it throws ModelCallLimitExceededException (whose message reports counts like "3/3 thread, 5/5 run") before another LLM call is made, protecting against runaway token spend.

Source

Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/hook/modelcalllimit/ModelCallLimitHook.java:74

	public static Builder builder() {
		return new Builder();
	}

	@Override
	public CompletableFuture<Map<String, Object>> beforeModel(OverAllState state, RunnableConfig config) {
		// Read current counts from context
		int threadModelCallCount = config.context().containsKey(THREAD_COUNT_KEY)
				? (int) config.context().get(THREAD_COUNT_KEY) : 0;
		int runModelCallCount = config.context().containsKey(RUN_COUNT_KEY)
				? (int) config.context().get(RUN_COUNT_KEY) : 0;

		// Check if limits are already exceeded (before making the call)
		boolean threadLimitExceeded = threadLimit != null && threadModelCallCount >= threadLimit;
		boolean runLimitExceeded = runLimit != null && runModelCallCount >= runLimit;

		if (threadLimitExceeded || runLimitExceeded) {
			if (exitBehavior == ExitBehavior.ERROR) {
				throw new ModelCallLimitExceededException(
						threadModelCallCount,
						runModelCallCount,
						threadLimit,
						runLimit
				);
			}
			else if (exitBehavior == ExitBehavior.END) {
				// Add message indicating limit was exceeded and jump to end
				String message = buildLimitExceededMessage(
						threadModelCallCount,
						runModelCallCount,
						threadLimit,
						runLimit
				);

				List<Message> messages = new ArrayList<>();
				messages.add(new AssistantMessage(message));

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Catch ModelCallLimitExceededException and treat it as a terminal state (summarize, notify user, or reset the thread).
  2. Raise threadLimit/runLimit in the hook builder if the limit was set too aggressively.
  3. Switch exitBehavior to a non-ERROR value (e.g. END) if exceeding the limit should stop gracefully instead of throwing.
  4. Reset or start a new thread when the per-thread count is exhausted.

Example fix

// before
ModelCallLimitHook hook = ModelCallLimitHook.builder().threadLimit(10).runLimit(5).exitBehavior(ExitBehavior.ERROR).build();
// after
ModelCallLimitHook hook = ModelCallLimitHook.builder().threadLimit(50).runLimit(20).exitBehavior(ExitBehavior.END).build();
Defensive patterns

Strategy: try-catch

Validate before calling

if (hook instanceof ModelCallLimitHook) { /* verify limits vs expected agent loop length */ 
  int expectedCalls = estimateModelCalls(agent);
  assert expectedCalls < configuredLimit; }

Try / catch

try {
    agent.invoke(inputs);
} catch (ModelCallLimitExceededException e) {
    log.warn("Limit hit: {} thread, {} run", e.getThreadCount(), e.getRunCount());
    // terminate gracefully / notify / checkpoint
}

Prevention

When it happens

Trigger: Running an agent with ModelCallLimitHook configured with threadLimit and/or runLimit and ExitBehavior.ERROR, where the model call count for the thread or current run reaches the limit before the next model invocation.

Common situations: Long-running conversational threads that accumulate calls across runs and hit threadLimit; loop/iterative agents that call the model many times per run and hit runLimit; setting low limits for cost control in tests then deploying unchanged to production.

Related errors


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