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

1) Build a ChatClientRequest with a null context value, e.g. context("tenantId", null).
2) Use a ChatClient with default advisors (or directly use ChatModelStreamAdvisor/ChatModelCallAdvisor).
3) Call .stream(...) or .call(...).
4) Map.copyOf throws 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.java
  • spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/advisor/ChatModelCallAdvisor.java
  • spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/advisor/SafeGuardAdvisor.java
  • spring-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.