zylon-ai/private-gpt · error · ValueError
No user message found in conversation history.
Error message
No user message found in conversation history.
What it means
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.
Source
Thrown at private_gpt/components/chat/processors/chat_history/memory/utils/condenser.py:24
from private_gpt.components.chat.processors.chat_history.memory.utils.format import (
guarantee_valid_message_sequence,
)
from private_gpt.events.models import BasicContentBlockType, TextBlock
def build_condensed_history(
system_messages: list[ChatMessage],
conversation_history: list[ChatMessage],
) -> tuple[list[ChatMessage], list[BasicContentBlockType]]:
"""Build the final condensed chat history."""
last_user_message: ChatMessage | None = None
for msg in reversed(conversation_history):
if msg.role == MessageRole.USER:
last_user_message = msg
break
if not last_user_message:
raise ValueError("No user message found in conversation history.")
def is_truncated(msg: ChatMessage) -> bool:
if not msg.additional_kwargs:
return False
if "tldr" not in msg.additional_kwargs:
return False
tldr_value = msg.additional_kwargs["tldr"]
return bool(tldr_value)
truncated_blocks: list[BasicContentBlockType] = [
TextBlock(
text=messages_to_history_str([msg], show_index=False, show_role=False),
metadata={
"type": "tldr",
"role": msg.role,
"tldr_side": msg.additional_kwargs.get("tldr", "left")
if isinstance(msg.additional_kwargs.get("tldr"), str)
else "left",View on GitHub (pinned to 4a030776a3)
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
Example fix
# before
condensed, blocks = build_condensed_history(system_messages, conversation_history)
# after
if not any(m.role == MessageRole.USER for m in conversation_history):
conversation_history = [ChatMessage(role=MessageRole.USER, content="(continue)")] + conversation_history
condensed, blocks = build_condensed_history(system_messages, conversation_history) Defensive patterns
Strategy: validation
Validate before calling
def has_user_message(conversation_history: list[ChatMessage]) -> bool:
return any(m.role == MessageRole.USER for m in conversation_history)
assert has_user_message(conversation_history), "conversation must contain a user message" Type guard
def is_condensable_history(history: list[ChatMessage]) -> bool:
return any(m.role == MessageRole.USER for m in history) Try / catch
try:
condensed, blocks = build_condensed_history(system_messages, conversation_history)
except ValueError as e:
if "No user message found" in str(e):
conversation_history = [ChatMessage(role=MessageRole.USER, content="(continue)")] + conversation_history
condensed, blocks = build_condensed_history(system_messages, conversation_history)
else:
raise Prevention
- Validate role presence before entering condensation
- Never route user messages into the system-message bucket
- Add a DB constraint/shape check when persisting conversations
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Chat history does not contain any user messages.
- No user messages found in the chat history.
- Maximum number of iterations for condensing exceeded.
- The last user message exceeds the maximum length allowed.
- No user messages found after condensation.
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/8e5e8cdcefb9e491.
Report an issue: GitHub.