spring-projects/spring-ai · error · IllegalStateException

No CallAdvisors available to execute

Error message

No CallAdvisors available to execute

What it means

DefaultAroundAdvisorChain.nextCall() requires at least one CallAdvisor in the chain; calling nextCall on an empty chain throws IllegalStateException('No CallAdvisors available to execute'). The chain terminates advisor execution by design, and an empty chain means there is nothing to dispatch the request to.

Source

Thrown at spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/advisor/DefaultAroundAdvisorChain.java:102

		this.observationRegistry = observationRegistry;
		this.callAdvisors = callAdvisors;
		this.streamAdvisors = streamAdvisors;
		this.originalCallAdvisors = List.copyOf(callAdvisors);
		this.originalStreamAdvisors = List.copyOf(streamAdvisors);
		this.observationConvention = observationConvention != null ? observationConvention
				: DEFAULT_OBSERVATION_CONVENTION;
	}

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

	@Override
	public ChatClientResponse nextCall(ChatClientRequest chatClientRequest) {
		Assert.notNull(chatClientRequest, "the chatClientRequest cannot be null");

		if (this.callAdvisors.isEmpty()) {
			throw new IllegalStateException("No CallAdvisors available to execute");
		}

		var advisor = this.callAdvisors.pop();

		var observationContext = AdvisorObservationContext.builder()
			.advisorName(advisor.getName())
			.chatClientRequest(chatClientRequest)
			.order(advisor.getOrder())
			.build();

		return AdvisorObservationDocumentation.AI_ADVISOR
			.observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
					this.observationRegistry)
			.observe(() -> {
				var chatClientResponse = advisor.adviseCall(chatClientRequest, this);
				observationContext.setChatClientResponse(chatClientResponse);
				return chatClientResponse;
			});

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Register at least one CallAdvisor when building the chain (e.g. ToolAdvisor or a request/response advisor).
  2. Create a fresh chain per request instead of reusing a popped/drained chain instance.
  3. If you only need streaming, use the stream path rather than nextCall.
  4. Check the chain's size/emptiness before invoking nextCall in custom code.

Example fix

// before
var chain = DefaultAroundAdvisorChain.builder(registry).build();
chain.nextCall(request);
// after
var chain = DefaultAroundAdvisorChain.builder(registry)
    .advisors(List.of(myCallAdvisor)).build();
chain.nextCall(request);
Defensive patterns

Strategy: validation

Validate before calling

if (chain == null /* or chain size not yet known */) {
  // build the chain with at least one CallAdvisor before calling nextCall
}
var chain = DefaultAroundAdvisorChain.builder(registry).advisors(List.of(callAdvisor)).build();

Try / catch

try { chain.nextCall(request); } catch (IllegalStateException e) { if (e.getMessage().contains("No CallAdvisors")) { /* rebuild chain with advisors */ } throw e; }

Prevention

When it happens

Trigger: Invoking nextCall() on a DefaultAroundAdvisorChain built without any CallAdvisors, or after all advisors have already been popped, or calling nextCall twice on the same chain.

Common situations: Building an advisor chain with only StreamRequestResponseAdvisors (no call advisors), reusing a chain across two calls so it's already drained, or misusing the internal API directly in tests.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/cf188507ce2ced81. Report an issue: GitHub.