zylon-ai/private-gpt · error · ValueError

Block cannot be None: {message}

Error message

Block cannot be None: {message}

What it means

When message.content is a list of content blocks, the ChatBody validator walks the blocks and rejects any None entry. A null block means malformed block serialization upstream (e.g. a block that failed to construct was appended as None); the error message includes the whole message for context.

Source

Thrown at private_gpt/server/chat/chat_models.py:321

        if self.frequency_penalty and self.frequency_penalty < 0:
            self.frequency_penalty = None

        if self.seed and self.seed < 0:
            self.seed = None

        if self.max_tokens is not None and self.max_tokens <= 0:
            self.max_tokens = None

        if not self.messages:
            raise ValueError("Messages cannot be empty")

        for message in self.messages:
            if not message.content:
                raise ValueError(f"Message content cannot be empty: {message}")
            if isinstance(message.content, list):
                for block in message.content:
                    if block is None:
                        raise ValueError(f"Block cannot be None: {message}")

        if self.messages[-1].role not in self._valid_last_message_roles:
            raise ValueError(
                f"Last message role must be one of {self._valid_last_message_roles}, but got {self.messages[-1].role}"
            )

        # Check tools and tool choice
        if self.tools and self.tool_choice and self.tool_choice.type == "tool":
            if not self.tools:
                raise ValueError("Tool choice is set, but no tools are provided.")
            if self.tool_choice.name not in [tool.name for tool in self.tools]:
                raise ValueError(
                    f"Tool choice '{self.tool_choice}' is not in the provided tools."
                )

        if not self.tools and self.tool_context:
            raise ValueError(
                "Tool context is provided, but no tools are specified. "

View on GitHub (pinned to 4a030776a3)

Solutions

  1. Filter nulls out of content arrays before sending: content=[b for b in blocks if b is not None].
  2. Fix the block serialization so unsupported blocks are skipped, not emitted as null.
  3. Validate stored history on load and drop/repair null blocks.

Example fix

# before
content=[block for block in raw_blocks]  # raw_blocks may contain None

# after
content=[block for block in raw_blocks if block is not None]
Defensive patterns

Strategy: validation

Validate before calling

for m in messages:
    if isinstance(m.get('content'), list):
        m['content'] = [b for b in m['content'] if b is not None]

Type guard

def blocks_valid(content: list | str) -> bool:
    if isinstance(content, list):
        return all(b is not None for b in content)
    return bool(content)

Try / catch

except ValidationError as e:
    if 'Block cannot be None' in str(e):
        strip_null_blocks(messages); retry_request()
    else:
        raise

Prevention

When it happens

Trigger: Content arrays containing literal null: {"role":"user","content":[null, {"type":"text","text":"hi"}]}; JSON built by mapping over blocks where one element failed conversion to None; deserializing stored conversations with missing block entries.

Common situations: Storing chat history as sparse arrays (deleted blocks become null); client SDKs that emit null for unsupported block types; partial block JSON round-trips.

Related errors


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