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
- Register at least one CallAdvisor when building the chain (e.g. ToolAdvisor or a request/response advisor).
- Create a fresh chain per request instead of reusing a popped/drained chain instance.
- If you only need streaming, use the stream path rather than nextCall.
- 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
- Always register at least one CallAdvisor in the chain.
- Never reuse a chain instance after it has been drained; build a new one per request.
- Use the stream API for streaming flows instead of nextCall.
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
- At most one ToolAdvisor is allowed in the advisor chain, but
- The specified advisor is not part of the chain:
- mutate() must be overridden to return the most concrete Buil
- Async complete methods should use McpAsyncRequestContext ins
- Method cannot have more than one McpMeta parameter: ${method
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/cf188507ce2ced81.
Report an issue: GitHub.