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

  1. Filter out empty/None messages from chat_history before calling repair_*
  2. Inspect get_user_blocks output on the failing history to find which boundary is empty
  3. 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

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


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