zylon-ai/private-gpt · error · ValueError
No user messages found after condensation.
Error message
No user messages found after condensation.
What it means
Post-condensation invariant check in ConversationCondenser.condense: after the chosen strategy runs, it verifies the result still contains at least one message with role 'user'. The condenser's contract is to always preserve the last user message; if the condensed output lost all user messages (e.g. a summarizer replaced them or the right-side trim removed them), it raises rather than returning a userless history.
Source
Thrown at private_gpt/components/chat/processors/chat_history/memory/strategies/condenser.py:818
left_tokens=left_tokens,
last_user_tokens=last_user_message_tokens,
right_tokens=right_tokens,
)
else:
chat_history = await self._condense_from_right(
llm=llm,
tokenizer_fn=tokenizer,
chat_history=chat_history,
max_length=max_length,
left_tokens=left_tokens,
last_user_tokens=last_user_message_tokens,
right_tokens=right_tokens,
)
# After condensation, we ensure that the last user message is preserved
# and the chat history is within the maximum length
if not any(msg.role == "user" for msg in chat_history):
raise ValueError("No user messages found after condensation.")
if not last_user_message:
raise ValueError("No last user message found after condensation.")
tokens = await self._get_messages_tokens(chat_history, tokenizer_fn=tokenizer)
if tokens > max_length:
raise ValueError(
"Condensed chat history exceeds maximum length after applying condensation strategy."
)
return chat_history
View on GitHub (pinned to 4a030776a3)
Solutions
- If using a custom strategy, re-append the original last user message to the condensed result before returning it.
- Check the summarizer prompt: the summary must be returned as assistant/system messages with the user turn retained.
- Inspect the condensed history in a debug hook to see where the user role disappeared.
- Catch the ValueError and fall back to returning the uncondensed (possibly truncated) history to keep the chat alive.
Example fix
// before
def condense(history):
return [ChatMessage(role="assistant", content=summarize(history))] # loses user turn
// after
def condense(history):
last_user = [m for m in history if m.role == "user"][-1]
return [ChatMessage(role="assistant", content=summarize(history)), last_user] Defensive patterns
Strategy: validation
Validate before calling
condensed = await strategy.condense(history)
if not any(m.role == "user" for m in condensed):
condensed = condensed + [last_user_message] # re-assert pivot Type guard
def retains_user_turn(messages: list[ChatMessage]) -> bool:
return any(m.role == "user" for m in messages) Try / catch
try:
return await condenser.condense(history)
except ValueError as e:
if "No user messages found after" in str(e):
return history[-keep_n:] # fallback preserving the last user turn
raise Prevention
- Custom strategies must re-append the last user message to their output.
- Test strategies with single-user-message fixtures.
- Summaries must be assistant/system role; never replace the user turn.
- Validate condensed output with a has-user check before returning.
When it happens
Trigger: LLM summarization of the left side producing messages all labelled assistant/system; a condense strategy bug returning only summarized content; histories where the last user message was in the right segment and got trimmed; empty result from the condense strategy.
Common situations: Custom condense strategies that rebuild history without role preservation; prompt templates for summarization instructing a single assistant summary; upstream tests feeding role-less or assistant-only histories; strategy output parsing dropping roles.
Related errors
- No last user message found after condensation.
- No user messages found in the chat history.
- Maximum number of iterations for condensing exceeded.
- The last user message exceeds the maximum length allowed.
- Condensed chat history exceeds maximum length after applying
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/8651e9b83bf92efb.
Report an issue: GitHub.