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

  1. Ensure the input history contains at least one MessageRole.USER message before condensing
  2. Fix upstream classification so user messages are never routed into system_messages
  3. 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

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


AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15). Data as JSON: /api/errors/8e5e8cdcefb9e491. Report an issue: GitHub.