zylon-ai/private-gpt · error · ValueError
Message content cannot be empty: {message}
Error message
Message content cannot be empty: {message} What it means
The ChatBody validator iterates all messages and rejects any message whose content is empty (empty string, empty list, or None). Unlike sampling params, empty content is not auto-corrected — it is a client error, surfaced as ValidationError/422 with the offending message embedded in the error text.
Source
Thrown at private_gpt/server/chat/chat_models.py:317
if self.presence_penalty and self.presence_penalty < 0:
self.presence_penalty = None
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."
)View on GitHub (pinned to 4a030776a3)
Solutions
- Drop empty messages client-side before sending (filter m for m.content).
- For tool-call-only assistant turns, set content to a non-empty placeholder or a content block list that is non-empty.
- Add a client-side assert that every message has truthy content.
Example fix
# before
messages = history + [{"role": "user", "content": user_text}] # user_text may be ""
# after
messages = [m for m in history if m.get("content")]
if user_text:
messages.append({"role": "user", "content": user_text}) Defensive patterns
Strategy: validation
Validate before calling
clean = [m for m in messages if m.get('content')]
assert clean, 'refusing to send: all messages empty' Type guard
def all_content_nonempty(msgs: list[dict]) -> bool:
return all(bool(m.get('content')) for m in msgs) Try / catch
except ValidationError as e:
if 'content cannot be empty' in str(e):
messages = [m for m in messages if m.get('content')]
resp = client.create(messages=messages, **rest)
else:
raise Prevention
- Filter falsy-content messages in the request builder
- Give tool-call-only assistant turns a placeholder content string
- Unit-test edge cases: empty string content, empty block list
When it happens
Trigger: A message like {"role":"user","content":""} or {"role":"assistant","content":[]} in messages; clients that append an empty placeholder message before streaming; trimming logic that empties content instead of dropping the message.
Common situations: Chat UIs that submit before the user types anything; assistant messages serialized with content removed when tool_calls-only messages are converted; upstream services forwarding pre-emptied content fields.
Related errors
- Messages cannot be empty
- Invalid system item in list (dict): {item}
- 'oneOf' must be an array of schemas
- 'anyOf' must be an array of schemas
- 'allOf' must be an array of schemas
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/fade166faf2ab107.
Report an issue: GitHub.