{"record":{"id":"3bec3f318e1993a7","repo":"zylon-ai/private-gpt","slug":"no-last-user-message-found-after-condensation","errorCode":null,"errorMessage":"No last user message found after condensation.","messagePattern":"No last user message found after condensation\\.","errorType":"exception","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"private_gpt/components/chat/processors/chat_history/memory/strategies/condenser.py","lineNumber":821,"sourceCode":"            )\n        else:\n            chat_history = await self._condense_from_right(\n                llm=llm,\n                tokenizer_fn=tokenizer,\n                chat_history=chat_history,\n                max_length=max_length,\n                left_tokens=left_tokens,\n                last_user_tokens=last_user_message_tokens,\n                right_tokens=right_tokens,\n            )\n\n        # After condensation, we ensure that the last user message is preserved\n        # and the chat history is within the maximum length\n        if not any(msg.role == \"user\" for msg in chat_history):\n            raise ValueError(\"No user messages found after condensation.\")\n\n        if not last_user_message:\n            raise ValueError(\"No last user message found after condensation.\")\n\n        tokens = await self._get_messages_tokens(chat_history, tokenizer_fn=tokenizer)\n        if tokens > max_length:\n            raise ValueError(\n                \"Condensed chat history exceeds maximum length after applying condensation strategy.\"\n            )\n\n        return chat_history\n","sourceCodeStart":803,"sourceCodeEnd":830,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/private_gpt/components/chat/processors/chat_history/memory/strategies/condenser.py#L803-L830","documentation":"Post-condensation invariant check in ConversationCondenser.condense, adjacent to the user-message check: after condensing, last_user_message must still be non-empty. The algorithm captures the last user message before condensing and the result must retain it; an empty/falsy last_user_message means the strategy dropped the pivot message the condenser exists to protect, so it raises.","triggerScenarios":"A condense branch that returns an empty list; a custom strategy that filters out the last user message; code paths where the right-segment trim removes the captured user message and nothing re-inserts it; empty input history reaching condense despite upstream checks.","commonSituations":"Custom registered condense strategies with off-by-one slicing; strategies returning chat_history[:] vs chat_history[:-1] mistakes; tests with single-message fixtures where the pivot lands in a trimmed segment.","solutions":["Ensure the strategy output includes the original last user message object (append it explicitly after summarizing).","Return early for empty/short histories instead of running them through condensing.","Unit-test custom strategies with single-user-message and assistant-only fixtures to catch pivot loss.","Wrap condense() calls in try/except ValueError and fall back to naive truncation that keeps the last user turn."],"exampleFix":"// before\nreturn left_summary + right_messages[:-1]   # pivot user message dropped\n\n// after\nreturn left_summary + [last_user_message] + right_messages","handlingStrategy":"validation","validationCode":"condensed = await strategy.condense(history)\nif last_user_message not in condensed:\n    condensed.append(last_user_message)","typeGuard":"def retains_last_user(condensed: list[ChatMessage], pivot: ChatMessage) -> bool:\n    return any(m is pivot or m == pivot for m in condensed)","tryCatchPattern":"try:\n    return await condenser.condense(history)\nexcept ValueError as e:\n    if \"last user message\" in str(e):\n        return [summary_of(history), pivot] if history else history\n    raise","preventionTips":["Never slice the pivot user message out with [:-1]-style trims.","Unit-test strategies with one-message and assistant-only histories.","Explicitly append the captured last_user_message to strategy output.","Fall back to truncated history that keeps the pivot on invariant failure."],"tags":["condensation","invariant","chat-history","roles","post-validation"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}