报
OpenAI API key must be set不代表 key 没传,而是 starter 的自动配置在启动阶段就校验spring.ai.openai.api-key,跟你之后在代码里new不new没关系。想在运行时动态传 key,得先关掉自动配置。
Issue & Env(案发现场与环境)
- 业务场景:用
spring-ai-starter-model-openai接 OpenAI,想在代码里动态创建OpenAiChatModel并传 key,不写进 application.yml。 - 环境:Spring AI 1.1.0(issue #4962 报告版本),Spring Boot 3.x,spring-core 6.2.6。
StackTrace(堆栈日志)
启动阶段就挂,报错文案把两个合法 key 名都点出来了。报错原文:
java.lang.IllegalArgumentException: OpenAI API key must be set. Use the connection property: spring.ai.openai.api-key or spring.ai.openai.chat.api-key property.
它在应用上下文创建 Bean 时抛出,完整堆栈:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'openAiChatModel' defined in class path resource [org/springframework/ai/autoconfigure/openai/OpenAiAutoConfiguration.class]: Failed to instantiate [org.springframework.ai.openai.OpenAiChatModel]: Factory method 'openAiChatModel' threw exception with message: OpenAI API key must be set. Use the connection property: spring.ai.openai.api-key or spring.ai.openai.chat.api-key property.
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:638)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1355)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1217)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:325)
Caused by: java.lang.IllegalArgumentException: OpenAI API key must be set. Use the connection property: spring.ai.openai.api-key or spring.ai.openai.chat.api-key property.
at org.springframework.util.Assert.hasText(Assert.java:253) ~[spring-core-6.2.6.jar:6.2.6]
at org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration.openAiChatModel(OpenAiAutoConfiguration.java:151)
Reproduce(复现代码)
starter 装进项目:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
<version>1.1.0</version>
</dependency>
配置文件里不写 key,改成代码里动态 new:
@GetMapping("/call/openai")
public String call() {
OpenAiChatModel chatModel = OpenAiChatModel.builder()
.openAiApi(OpenAiApi.builder()
.apiKey("sk-xxxx")
.baseUrl("https://your-relay.example.com/v1")
.build())
.build();
return chatModel.call("hi");
}
启动即报上面的 key 缺失。你明明在代码里传了 key,报错却打在启动阶段,而不是 call() 执行的时候。
Root Cause(根因分析)
spring-ai-starter-model-openai 靠 Spring Boot 自动配置装配 ChatModel Bean。这个自动配置在启动阶段就执行 Assert.hasText,检查 spring.ai.openai.api-key(或聊天专用 spring.ai.openai.chat.api-key)有没有值。
两条线是脱钩的:自动配置只看「配置文件里有没有 key」;你在 controller 里 new 的 OpenAiChatModel 是另一个对象,管不到自动配置。启动时自动配置先跑,发现属性为空,直接抛 IllegalArgumentException,应用上下文起不来,根本轮不到 call()。
报错文案写全了合法 key 名,说明这是设计行为,不是 bug。maintainer 在 issue #4962 的答复是:要运行时自己建模型就关掉自动配置,想省事就把 key 写进配置。
The Fix(解决方案)
正解:把 key 写进配置,交给自动装配
最省事,key 走环境变量,别硬编码:
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
框架自己建好 ChatModel Bean,你直接注入用。聊天模型还能用 spring.ai.openai.chat.api-key 单独覆盖公共 key。
真要动态传 key:关掉自动配置
要按租户动态选 key,就把 OpenAI 聊天自动配置关掉,再自己建 Bean。官方文档给的方式是把模型选择置空:
spring:
ai:
model:
chat: none
或者在启动类上排除自动配置类:
@SpringBootApplication(exclude = OpenAiAutoConfiguration.class)
public class Application {}
自动配置关了,启动时不再校验 key,运行时怎么动态 new 都行。
