Environment
| Component |
Version |
| google-adk / google-adk-spring-ai |
1.9.0 |
| Spring AI (BOM) |
2.0.1 |
| Spring Boot |
4.0.2 |
| Java |
25 |
Minimal reproduction
pom.xml (relevant parts):
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.2</version>
</parent>
<properties>
<java.version>25</java.version>
<spring-ai.version>2.0.1</spring-ai.version>
<adk.version>1.9.0</adk.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.google.adk</groupId>
<artifactId>google-adk</artifactId>
<version>${adk.version}</version>
</dependency>
<dependency>
<groupId>com.google.adk</groupId>
<artifactId>google-adk-spring-ai</artifactId>
<version>${adk.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Application.java:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
LlmAgent agent(SpringAI springAI) {
return LlmAgent.builder()
.name("demo-agent")
.model(springAI)
.instruction("You are a helpful assistant.")
.build();
}
}
application.yaml (dummy values are enough — the failure happens at bean-definition processing, before any remote call):
spring:
ai:
openai:
base-url: https://api.openai.com
api-key: dummy
chat:
model: gpt-4o-mini
Run: mvn spring-boot:run
Actual behavior
Application fails to start:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method agent in com.example.demo.DemoApplication required a bean
of type 'com.google.adk.models.springai.SpringAI' that could not be found.
Action:
Consider defining a bean of type 'com.google.adk.models.springai.SpringAI' in your configuration.
Running with --debug shows the root cause — the OpenAI ChatModel bean definition is registered, but SpringAIAutoConfiguration is processed before it, so all three SpringAI @Bean methods are skipped:
OpenAiChatAutoConfiguration matched:
- @ConditionalOnProperty (spring.ai.model.chat=openai) matched
OpenAiChatAutoConfiguration#openAiChatModel matched:
- @ConditionalOnMissingBean ... did not find any beans (OnBeanCondition)
SpringAIAutoConfiguration#springAIWithBothModels:
Did not match:
- @ConditionalOnBean (types: ...ChatModel, ...StreamingChatModel) did not find any beans
SpringAIAutoConfiguration#springAIWithChatModel:
Did not match:
- @ConditionalOnBean (types: ...ChatModel) did not find any beans of type ...ChatModel (OnBeanCondition)
SpringAIAutoConfiguration#springAIWithStreamingModel:
Did not match:
- @ConditionalOnBean (types: ...StreamingChatModel) did not find any beans
Note springAIEmbedding has the same problem (@ConditionalOnBean(EmbeddingModel.class)) — it fails silently (no bean, no error), which makes it even harder to notice.
Expected behavior
SpringAIAutoConfiguration registers a SpringAI bean whenever a ChatModel (and/or StreamingChatModel) is defined by Spring AI model auto-configurations, regardless of which provider starter is used.
Root cause
SpringAIAutoConfiguration guards its @Bean methods with @ConditionalOnBean(...), but declares no ordering relative to the auto-configurations that may provide those beans. Per the Spring Boot reference documentation, the result of @ConditionalOnBean depends on what has been processed so far, so an auto-configuration relying on it must be ordered after the configurations that register the matched beans.
google-adk-spring-ai 1.9.0 is built against Spring AI 2.x / Boot 4 (its POM depends on spring-ai-model:2.0.1 and spring-boot-autoconfigure:4.0.2), and since it compiles only against spring-ai-model (no provider modules), the Class-based after = ... attribute is not usable here — the string-based afterName attribute exists exactly for this case (non-existent class names are silently ignored by the sorter).
Suggested fix
Add an afterName list covering the Spring AI model auto-configurations, mirroring what Spring AI 1.x's own ChatClientAutoConfiguration did for the identical scenario (@ConditionalOnBean(ChatModel) with an unpredictable provider):
-@AutoConfiguration
+@AutoConfiguration(afterName = {
+ "org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration",
+ "org.springframework.ai.model.openai.autoconfigure.OpenAiEmbeddingAutoConfiguration",
+ // remaining Spring AI 2.x model auto-configurations, e.g.:
+ // org.springframework.ai.model.anthropic.autoconfigure.AnthropicChatAutoConfiguration
+ // org.springframework.ai.model.ollama.autoconfigure.OllamaChatAutoConfiguration
+ // ... (package pattern: org.springframework.ai.model.<provider>.autoconfigure.*)
+})
@ConditionalOnClass({SpringAI.class, ChatModel.class})
@ConditionalOnProperty(prefix = "adk.spring-ai.auto-configuration", name = "enabled",
havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties({SpringAIProperties.class})
public class SpringAIAutoConfiguration {
(Verified against Spring AI 2.0.1: org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration / OpenAiEmbeddingAutoConfiguration. The other provider names above follow the 2.x package pattern and should be double-checked against the current source.)
An alternative design — the one Spring AI 2.0 itself adopted — is to drop the @ConditionalOnBean guards entirely and take ChatModel/StreamingChatModel as @Bean method parameters, which removes the ordering sensitivity altogether. The afterName fix is the smaller, backward-compatible change, though.
Workaround (for users on 1.9.0)
Define the SpringAI bean in application code — user-defined beans are processed before auto-configurations, and ADK's @ConditionalOnMissingBean(SpringAI.class) then backs off:
@Bean
SpringAI springAI(ChatModel chatModel) {
return new SpringAI(chatModel, "gpt-4o-mini");
}
This works with any provider (it consumes the ChatModel abstraction), but every project using ADK + Spring AI has to copy it, so it is not a substitute for the upstream fix.
References
- Spring Boot reference — Creating your own auto-configuration / condition annotations: ordering matters for
@ConditionalOnBean
- Spring AI 1.x
ChatClientAutoConfiguration: same pattern solved with afterName over all provider auto-configurations
- Spring AI 2.0.1
ChatClientAutoConfiguration: same problem avoided via @Bean parameter injection instead of @ConditionalOnBean
Environment
Minimal reproduction
pom.xml (relevant parts):
Application.java:
application.yaml (dummy values are enough — the failure happens at bean-definition processing, before any remote call):
Run:
mvn spring-boot:runActual behavior
Application fails to start:
Running with
--debugshows the root cause — the OpenAIChatModelbean definition is registered, butSpringAIAutoConfigurationis processed before it, so all threeSpringAI@Beanmethods are skipped:Note
springAIEmbeddinghas the same problem (@ConditionalOnBean(EmbeddingModel.class)) — it fails silently (no bean, no error), which makes it even harder to notice.Expected behavior
SpringAIAutoConfigurationregisters aSpringAIbean whenever aChatModel(and/orStreamingChatModel) is defined by Spring AI model auto-configurations, regardless of which provider starter is used.Root cause
SpringAIAutoConfigurationguards its@Beanmethods with@ConditionalOnBean(...), but declares no ordering relative to the auto-configurations that may provide those beans. Per the Spring Boot reference documentation, the result of@ConditionalOnBeandepends on what has been processed so far, so an auto-configuration relying on it must be ordered after the configurations that register the matched beans.google-adk-spring-ai1.9.0 is built against Spring AI 2.x / Boot 4 (its POM depends onspring-ai-model:2.0.1andspring-boot-autoconfigure:4.0.2), and since it compiles only againstspring-ai-model(no provider modules), the Class-basedafter = ...attribute is not usable here — the string-basedafterNameattribute exists exactly for this case (non-existent class names are silently ignored by the sorter).Suggested fix
Add an
afterNamelist covering the Spring AI model auto-configurations, mirroring what Spring AI 1.x's ownChatClientAutoConfigurationdid for the identical scenario (@ConditionalOnBean(ChatModel)with an unpredictable provider):(Verified against Spring AI 2.0.1:
org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration/OpenAiEmbeddingAutoConfiguration. The other provider names above follow the 2.x package pattern and should be double-checked against the current source.)An alternative design — the one Spring AI 2.0 itself adopted — is to drop the
@ConditionalOnBeanguards entirely and takeChatModel/StreamingChatModelas@Beanmethod parameters, which removes the ordering sensitivity altogether. TheafterNamefix is the smaller, backward-compatible change, though.Workaround (for users on 1.9.0)
Define the
SpringAIbean in application code — user-defined beans are processed before auto-configurations, and ADK's@ConditionalOnMissingBean(SpringAI.class)then backs off:This works with any provider (it consumes the
ChatModelabstraction), but every project using ADK + Spring AI has to copy it, so it is not a substitute for the upstream fix.References
@ConditionalOnBeanChatClientAutoConfiguration: same pattern solved withafterNameover all provider auto-configurationsChatClientAutoConfiguration: same problem avoided via@Beanparameter injection instead of@ConditionalOnBean