zylon-ai/private-gpt · error · ValueError
User block {i} is empty.
Error message
User block {i} is empty. What it means
During _assert_user_blocks, get_user_blocks(chat_history) produced a block (list of messages between user-message boundaries) that is empty. An empty block means the splitting logic created a boundary with no messages, which indicates malformed or inconsistent history structure. The repair pipeline treats this as unrecoverable and raises.
Source
Thrown at private_gpt/components/chat/processors/chat_history/memory/utils/repairs.py:131
if assistant_is_valid and tool_is_valid
else []
)
def _assert_user_blocks(chat_history: list[ChatMessage], strict: bool) -> None:
"""A user block should follow a regex pattern.
Strict mode: (user, (assistant, tool)*, assistant)
Non-strict mode: (user, (assistant, tool)*, assistant?)
"""
if not chat_history:
return
matrix = get_user_blocks(chat_history)
for i, block in enumerate(matrix):
if not block:
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(View on GitHub (pinned to 4a030776a3)
Solutions
- Filter out empty/None messages from chat_history before calling repair_*
- Inspect get_user_blocks output on the failing history to find which boundary is empty
- Fix the producer that persists malformed histories (validate on write)
Example fix
# before repaired = repair_without_tools(chat_history) # after chat_history = [m for m in chat_history if m is not None and m.content] repaired = repair_without_tools(chat_history)
Defensive patterns
Strategy: validation
Validate before calling
blocks = get_user_blocks(chat_history) assert all(len(b) > 0 for b in blocks), "empty user block detected" assert chat_history, "history itself empty"
Type guard
def has_no_empty_blocks(history: list[ChatMessage]) -> bool:
return all(len(b) > 0 for b in get_user_blocks(history)) Try / catch
try:
repaired = repair_without_tools(chat_history)
except ValueError as e:
if "is empty" in str(e):
chat_history = [m for m in chat_history if m is not None and m.content]
repaired = repair_without_tools(chat_history)
else:
raise Prevention
- Sanitize persisted histories (no None/empty messages) before repair
- Validate on write, not only on read
- Unit-test repair entry points with degenerate histories
When it happens
Trigger: Calling repair_without_tools() or repair_with_tools() (which call _assert_user_blocks) on a history where get_user_blocks yields an empty list element — typically degenerate role sequences or empty message lists at block boundaries.
Common situations: Histories assembled from multiple sources (DB + cache) with dropped messages; empty-message entries inserted by a buggy producer; history truncated mid-block.
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
- User block {i} does not end with an assistant or tool messag
- Tool message {j} in user block {i} does not follow an assist
- Message {j} in user block {i} is neither an assistant nor a
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/38c18b84603245f8.
Report an issue: GitHub.