{"record":{"id":"8e5e8cdcefb9e491","repo":"zylon-ai/private-gpt","slug":"no-user-message-found-in-conversation-history","errorCode":null,"errorMessage":"No user message found in conversation history.","messagePattern":"No user message found in conversation history\\.","errorType":"exception","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"private_gpt/components/chat/processors/chat_history/memory/utils/condenser.py","lineNumber":24,"sourceCode":"from private_gpt.components.chat.processors.chat_history.memory.utils.format import (\n    guarantee_valid_message_sequence,\n)\nfrom private_gpt.events.models import BasicContentBlockType, TextBlock\n\n\ndef build_condensed_history(\n    system_messages: list[ChatMessage],\n    conversation_history: list[ChatMessage],\n) -> tuple[list[ChatMessage], list[BasicContentBlockType]]:\n    \"\"\"Build the final condensed chat history.\"\"\"\n    last_user_message: ChatMessage | None = None\n    for msg in reversed(conversation_history):\n        if msg.role == MessageRole.USER:\n            last_user_message = msg\n            break\n\n    if not last_user_message:\n        raise ValueError(\"No user message found in conversation history.\")\n\n    def is_truncated(msg: ChatMessage) -> bool:\n        if not msg.additional_kwargs:\n            return False\n        if \"tldr\" not in msg.additional_kwargs:\n            return False\n        tldr_value = msg.additional_kwargs[\"tldr\"]\n        return bool(tldr_value)\n\n    truncated_blocks: list[BasicContentBlockType] = [\n        TextBlock(\n            text=messages_to_history_str([msg], show_index=False, show_role=False),\n            metadata={\n                \"type\": \"tldr\",\n                \"role\": msg.role,\n                \"tldr_side\": msg.additional_kwargs.get(\"tldr\", \"left\")\n                if isinstance(msg.additional_kwargs.get(\"tldr\"), str)\n                else \"left\",","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/private_gpt/components/chat/processors/chat_history/memory/utils/condenser.py#L6-L42","documentation":"build_condensed_history scans conversation_history in reverse for the last user message and refuses to proceed if none exists, since the condensed history format requires at least one user turn. This is an input-shape invariant: the conversation portion must contain a user message.","triggerScenarios":"Calling build_condensed_history(system_messages, conversation_history) where every message in conversation_history has role != MessageRole.USER (e.g. only assistant/system messages were classified as conversation).","commonSituations":"Upstream splitting logic (get_system_and_conversation_messages) misclassifying the only user message as a system message; history loaded from storage missing the user turn; a conversation that genuinely starts with an assistant greeting.","solutions":["Ensure the input history contains at least one MessageRole.USER message before condensing","Fix upstream classification so user messages are never routed into system_messages","If assistant-first histories are legitimate, synthesize or skip condensation for them"],"exampleFix":"# before\ncondensed, blocks = build_condensed_history(system_messages, conversation_history)\n\n# after\nif not any(m.role == MessageRole.USER for m in conversation_history):\n    conversation_history = [ChatMessage(role=MessageRole.USER, content=\"(continue)\")] + conversation_history\ncondensed, blocks = build_condensed_history(system_messages, conversation_history)","handlingStrategy":"validation","validationCode":"def has_user_message(conversation_history: list[ChatMessage]) -> bool:\n    return any(m.role == MessageRole.USER for m in conversation_history)\n\nassert has_user_message(conversation_history), \"conversation must contain a user message\"","typeGuard":"def is_condensable_history(history: list[ChatMessage]) -> bool:\n    return any(m.role == MessageRole.USER for m in history)","tryCatchPattern":"try:\n    condensed, blocks = build_condensed_history(system_messages, conversation_history)\nexcept ValueError as e:\n    if \"No user message found\" in str(e):\n        conversation_history = [ChatMessage(role=MessageRole.USER, content=\"(continue)\")] + conversation_history\n        condensed, blocks = build_condensed_history(system_messages, conversation_history)\n    else:\n        raise","preventionTips":["Validate role presence before entering condensation","Never route user messages into the system-message bucket","Add a DB constraint/shape check when persisting conversations"],"tags":["chat-history","validation","user-message","condensation"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}