-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Open
Description
Bug description
ChatClientRequest only forbids null keys, not null values. The advisors (ChatModelStreamAdvisor, ChatModelCallAdvisor, SafeGuardAdvisor) call Map.copyOf(request.context()), and that blows up with NullPointerException if any value is null. The call/stream fails before the model is invoked.
Environment
- Spring AI: main (latest)
- Java: 17 (also seen on 21)
- Vector store: not involved
Steps to reproduce
- Build a
ChatClientRequestwith a null context value, e.g.context("tenantId", null). - Use a
ChatClientwith default advisors (or directly useChatModelStreamAdvisor/ChatModelCallAdvisor). - Call
.stream(...)or.call(...). Map.copyOfthrows NPE inside the advisor.
Expected behavior
Either reject the request up front with a clear validation error, or avoid throwing NPE in the advisors when context contains null values.
Minimal complete reproducible example
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.prompt.Prompt;
class ChatClientRequestNullContextTests {
@Test
void nullContextValueTriggersNpeInAdvisor() {
ChatClientRequest request = ChatClientRequest.builder()
.prompt(new Prompt("hi"))
.context("tenantId", null) // null value
.build();
// Same failure point as in the advisors:
assertThatThrownBy(() -> java.util.Map.copyOf(request.context()))
.isInstanceOf(NullPointerException.class);
}
}Affected code
spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/advisor/ChatModelStreamAdvisor.javaspring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/advisor/ChatModelCallAdvisor.javaspring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/advisor/SafeGuardAdvisor.javaspring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/ChatClientRequest.java
Proposed resolution
Tighten ChatClientRequest validation to disallow null values in context, so invalid requests fail fast with a clear message, and the advisors can keep their immutable Map.copyOf calls.