zylon-ai/private-gpt · error · ValueError
User block {i} does not end with an assistant or tool messag
Error message
User block {i} does not end with an assistant or tool message: {block[-1]} What it means
Non-strict-mode user-block validation: a multi-message block must end with either an assistant or a tool message. It fires when the block ends with any other role — in practice a user message, meaning two consecutive user turns inside one block.
Source
Thrown at private_gpt/components/chat/processors/chat_history/memory/utils/repairs.py:149
raise ValueError(f"User block {i} is empty.")
# Validate the first and last messages in the block
if block[0].role != "user":
raise ValueError(
f"User block {i} does not start with a user message: {block[0]}"
)
if len(block) == 1:
# No more validation needed for a single user message block
continue
# Validate the last message in the block
if strict and block[-1].role != "assistant":
raise ValueError(
f"User block {i} does not end with an assistant message: {block[-1]}"
)
elif not strict and block[-1].role not in ["assistant", "tool"]:
raise ValueError(
f"User block {i} does not end with an assistant or tool message: {block[-1]}"
)
# Validate the tool messages
for j, message in enumerate(block[1:-1]):
if message.role == "tool":
if j == 0 or block[j].role != "assistant":
raise ValueError(
f"Tool message {j} in user block {i} does not follow an assistant message: {message}"
)
elif message.role != "assistant":
raise ValueError(
f"Message {j} in user block {i} is neither an assistant nor a tool: {message}"
)
def _assert_repair_without_tools(
chat_history: list[ChatMessage],View on GitHub (pinned to 4a030776a3)
Solutions
- Merge adjacent user messages into one before repair (merge_adjacent_messages exists in the module chain)
- Insert a synthetic assistant acknowledgement between consecutive user turns
- Drop orphan user turns that never got a response if they are noise
Example fix
# before repaired = repair_without_tools(chat_history, strict=False) # after from private_gpt.components.chat.processors.chat_history.memory.utils.merge import merge_adjacent_messages repaired = repair_without_tools(merge_adjacent_messages(chat_history), strict=False)
Defensive patterns
Strategy: validation
Validate before calling
from private_gpt.components.chat.processors.chat_history.memory.utils.merge import merge_adjacent_messages
history = merge_adjacent_messages(chat_history) # collapses consecutive user turns
blocks = get_user_blocks(history)
assert all(b[-1].role in ("assistant", "tool") for b in blocks if len(b) > 1) Type guard
def blocks_end_valid_nonstrict(history: list[ChatMessage]) -> bool:
return all(b[-1].role in ("assistant", "tool") for b in get_user_blocks(history) if len(b) > 1) Try / catch
try:
repaired = repair_without_tools(chat_history, strict=False)
except ValueError as e:
if "does not end with an assistant or tool message" in str(e):
repaired = repair_without_tools(merge_adjacent_messages(chat_history), strict=False)
else:
raise Prevention
- Always merge adjacent same-role messages before repair
- Debounce rapid user messages in the UI so turns don't interleave
- Persist an assistant ack even for errored turns
When it happens
Trigger: Calling repair_*(strict=False) on history where a user block's last message is a user message (user sent multiple messages without an assistant reply between them).
Common situations: Chat front-ends that let users fire messages rapidly; batch-loaded transcripts with missing assistant responses; message merging not applied before validation.
Related errors
- User block {i} does not start with a user message: {block[0]
- User block {i} does not end with an assistant message: {bloc
- Tool message {j} in user block {i} does not follow an assist
- Message {j} in user block {i} is neither an assistant nor a
- User block {i} is empty.
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/21f90740e7c39ea1.
Report an issue: GitHub.