{"record":{"id":"41d488616661a4a6","repo":"theonedev/onedev","slug":"conversation-context-lost","errorCode":null,"errorMessage":"Conversation context lost","messagePattern":"Conversation context lost","errorType":"exception","errorClass":"ExplicitException","httpStatus":null,"severity":"warning","filePath":"server-core/src/main/java/io/onedev/server/ai/DefaultChatService.java","lineNumber":317,"sourceCode":"\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onCompleteResponse(ChatResponse completeResponse) {\t\n\t\t\t\t\t\tThreadContext.bind(subject);\n\t\t\t\t\t\tvar responding = getResponding(sessionId, chatId, requestId);\n\t\t\t\t\t\tif (responding != null && responding.getContent() != null && thinkingBlockOpen) {\n\t\t\t\t\t\t\tresponding.content = ensureThinkingClosed(responding.getContent());\n\t\t\t\t\t\t\twebSocketService.notifyObservableChange(Chat.getPartialResponseObservable(chatId), null);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsessionService.runAsync(() -> {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tvar aiMessage = completeResponse.aiMessage();\n\t\t\t\t\t\t\t\tif (aiMessage.hasToolExecutionRequests()) {\n\t\t\t\t\t\t\t\t\tlangchain4jMessages.add(aiMessage);\n\t\t\t\t\t\t\t\t\tvar toolRequests = aiMessage.toolExecutionRequests();\n\t\t\t\t\t\t\t\t\tvar connectionRegistry = WebSocketSettings.Holder.get(application).getConnectionRegistry();\n\t\t\t\t\t\t\t\t\tvar connection = connectionRegistry.getConnection(application, sessionId, pageKey);\n\t\t\t\t\t\t\t\t\tif (connection == null || !connection.isOpen())\n\t\t\t\t\t\t\t\t\t\tthrow new ExplicitException(\"Conversation context lost\");\n\t\t\t\t\t\t\t\t\tfor (var toolRequest: toolRequests) {\n\t\t\t\t\t\t\t\t\t\tif (responseFuture.isDone())\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\tString toolName = toolRequest.name();\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tvar toolExecution = new AiToolExecution(toolName, ToolUtils.getToolArguments(toolRequest));\n\t\t\t\t\t\t\t\t\t\t\tconnection.sendMessage(toolExecution);\n\t\t\t\t\t\t\t\t\t\t\tvar toolExecutionFuture = toolExecution.getFuture();\n\t\t\t\t\t\t\t\t\t\t\tif (toolExecutionFuture == null)\n\t\t\t\t\t\t\t\t\t\t\t\tthrow new ExplicitException(\"Tool not found: \" + toolName);\n\t\t\t\t\t\t\t\t\t\t\twhile (true) {\n\t\t\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\t\t\tvar toolExecutionResult = toolExecutionFuture.get(1, TimeUnit.SECONDS);\n\t\t\t\t\t\t\t\t\t\t\t\t\ttoolExecutionResult.addToMessages(langchain4jMessages, toolRequest);\n\t\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t\t\t} catch (TimeoutException e) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (responseFuture.isDone())\n\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn;","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/ai/DefaultChatService.java#L299-L335","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: user closes page mid-stream -> exception\n// after: client keeps the chat panel mounted and reconnects before sending a message\n// (server-side guard in calling code)\ntry {\n    chatService.chat(...);\n} catch (ExplicitException e) {\n    if (e.getMessage().equals(\"Conversation context lost\")) {\n        reconnectWebSocketAndResend();\n    }\n}","handlingStrategy":"try-catch","validationCode":"// client-side before sending a chat message\nif (!ws || ws.readyState !== WebSocket.OPEN) reconnectChatSocket();","typeGuard":null,"tryCatchPattern":"try {\n    chatService.chat(chatId, prompt);\n} catch (ExplicitException e) {\n    if (\"Conversation context lost\".equals(e.getMessage())) {\n        // reopen chat page / re-establish WebSocket and resend\n    }\n}","preventionTips":["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"],"tags":["websocket","ai-chat","connection-lifecycle"],"backgroundTag":"connection-refused","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}