From c914ea2eb62187f66818f9f141650ff90c1d04a7 Mon Sep 17 00:00:00 2001 From: octo-patch Date: Sat, 18 Apr 2026 10:58:30 +0800 Subject: [PATCH] fix: handle null content in SSE stream chunks for REST and Zhipu AI listeners When local LLMs (e.g. llama.cpp) or Zhipu AI return SSE stream responses, the first chunk may contain only a "role" field with "content": null. This caused a NullPointerException in Message.setContent() due to @NonNull enforcement, making the entire AI chat session fail. Treat null content as an empty string (matching the behaviour of FastChatAIEventSourceListener) so the stream continues gracefully. Fixes #1808 Fixes #1788 --- .../ai/rest/listener/RestAIEventSourceListener.java | 5 +++++ .../ai/zhipu/listener/ZhipuChatAIEventSourceListener.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/rest/listener/RestAIEventSourceListener.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/rest/listener/RestAIEventSourceListener.java index 0509ac6fe..03dcd92b4 100644 --- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/rest/listener/RestAIEventSourceListener.java +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/rest/listener/RestAIEventSourceListener.java @@ -64,6 +64,11 @@ public void onEvent(EventSource eventSource, String id, String type, String data String text = chatCompletions.getChoices().get(0).getDelta()==null? chatCompletions.getChoices().get(0).getText() :chatCompletions.getChoices().get(0).getDelta().getContent(); + // Local LLMs (e.g. llama.cpp) may return null content in the first SSE chunk + // (only role is present). Treat null as empty string to avoid NullPointerException. + if (text == null) { + text = ""; + } message.setContent(text); sseEmitter.send(SseEmitter.event() .id(id) diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java index efbc9f827..dfbfb1573 100644 --- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java @@ -62,6 +62,11 @@ public void onEvent(EventSource eventSource, String id, String type, @NotNull St String text = chatCompletions.getChoices().get(0).getDelta()==null? chatCompletions.getChoices().get(0).getText() :chatCompletions.getChoices().get(0).getDelta().getContent(); + // The first SSE chunk may carry only "role" with null "content". + // Guard against NullPointerException from Message.setContent(). + if (text == null) { + text = ""; + } Message message = new Message(); message.setContent(text);