theonedev/onedev · warning · ExplicitException
Conversation context lost
Error message
Conversation context lost
What it means
OneDev's AI chat service streams tool calls from the LLM back to the browser over a WebSocket. When the model returns tool execution requests, the service looks up the originating WebSocket connection by session id and page key; if the connection is gone or closed, it aborts with this ExplicitException because the tool result can no longer be delivered to the client.
Source
Thrown at server-core/src/main/java/io/onedev/server/ai/DefaultChatService.java:317
@Override
public void onCompleteResponse(ChatResponse completeResponse) {
ThreadContext.bind(subject);
var responding = getResponding(sessionId, chatId, requestId);
if (responding != null && responding.getContent() != null && thinkingBlockOpen) {
responding.content = ensureThinkingClosed(responding.getContent());
webSocketService.notifyObservableChange(Chat.getPartialResponseObservable(chatId), null);
}
sessionService.runAsync(() -> {
try {
var aiMessage = completeResponse.aiMessage();
if (aiMessage.hasToolExecutionRequests()) {
langchain4jMessages.add(aiMessage);
var toolRequests = aiMessage.toolExecutionRequests();
var connectionRegistry = WebSocketSettings.Holder.get(application).getConnectionRegistry();
var connection = connectionRegistry.getConnection(application, sessionId, pageKey);
if (connection == null || !connection.isOpen())
throw new ExplicitException("Conversation context lost");
for (var toolRequest: toolRequests) {
if (responseFuture.isDone())
return;
String toolName = toolRequest.name();
try {
var toolExecution = new AiToolExecution(toolName, ToolUtils.getToolArguments(toolRequest));
connection.sendMessage(toolExecution);
var toolExecutionFuture = toolExecution.getFuture();
if (toolExecutionFuture == null)
throw new ExplicitException("Tool not found: " + toolName);
while (true) {
try {
var toolExecutionResult = toolExecutionFuture.get(1, TimeUnit.SECONDS);
toolExecutionResult.addToMessages(langchain4jMessages, toolRequest);
break;
} catch (TimeoutException e) {
if (responseFuture.isDone())
return;View on GitHub (pinned to d44925c47c)
Solutions
- Keep the chat page open until the AI response finishes; check the browser console for WebSocket disconnects before retrying
- Retry the conversation from a fresh chat page so a new WebSocket connection and pageKey are established
- If behind a reverse proxy, raise proxy read/send timeouts and enable WebSocket upgrade passthrough
- Check server logs for earlier WebSocket errors (connection registry drops) and adjust WebSocket idle timeout settings
Example fix
// before: user closes page mid-stream -> exception
// after: client keeps the chat panel mounted and reconnects before sending a message
// (server-side guard in calling code)
try {
chatService.chat(...);
} catch (ExplicitException e) {
if (e.getMessage().equals("Conversation context lost")) {
reconnectWebSocketAndResend();
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// client-side before sending a chat message if (!ws || ws.readyState !== WebSocket.OPEN) reconnectChatSocket();
Try / catch
try {
chatService.chat(chatId, prompt);
} catch (ExplicitException e) {
if ("Conversation context lost".equals(e.getMessage())) {
// reopen chat page / re-establish WebSocket and resend
}
} Prevention
- Keep the chat page open until responses complete
- Reconnect the WebSocket before each new chat message if it is closed
- Configure proxies/load balancers to allow long-lived WebSocket connections
- Watch server logs for WebSocket disconnect patterns
When it happens
Trigger: onCompleteResponse receives an AI message containing tool execution requests while connectionRegistry.getConnection(application, sessionId, pageKey) returns null or a closed connection — i.e. the user closed/refreshed the chat page or the WebSocket dropped mid-conversation.
Common situations: User navigates away or reloads the page while the AI response is still streaming; WebSocket idle timeouts or proxy/load-balancer disconnects; server restart between request and tool-call phase; session id or page key changed after a page re-render.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- Received empty response
- The request should have either 'pageId' or 'resourceName' pa
- Unsupported message type: {message.getClass().getName()}
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/41d488616661a4a6.
Report an issue: GitHub.