zylon-ai/private-gpt · error · Errors.InvalidRequest
No user message found in request.
Error message
No user message found in request.
What it means
Raised by the static helper _last_user_message when no message with role USER exists in request.messages. The validator scans messages in reverse and raises Errors.InvalidRequest if none matches. A chat request must contain at least one user message.
Source
Thrown at private_gpt/server/chat/interceptors/validator_request_interceptor.py:183
return
@staticmethod
def _extract_text(message: ChatMessage) -> str:
"""Extract normalized text from message blocks."""
parts = [
block.text.strip()
for block in message.blocks
if isinstance(block, TextBlock) and block.text and block.text.strip()
]
return "\n".join(parts)
@staticmethod
def _last_user_message(messages: list[ChatMessage]) -> ChatMessage:
"""Return the latest user message from request messages."""
for message in reversed(messages):
if message.role == MessageRole.USER:
return message
raise Errors.InvalidRequest("No user message found in request.")
@staticmethod
def _system_message_text(messages: list[ChatMessage]) -> str | None:
"""Return first system message text when present."""
for message in messages:
if message.role != MessageRole.SYSTEM:
continue
text = ValidatorRequestInterceptor._extract_text(message)
if text:
return text
return None
View on GitHub (pinned to 4a030776a3)
Solutions
- Ensure request.messages contains at least one ChatMessage with role == MessageRole.USER.
- Append the current user query before sending multi-turn history.
- Validate the payload client-side: assert any(m.role == 'user' for m in messages) before the call.
Example fix
# before messages = [ChatMessage(role=MessageRole.ASSISTANT, ...)] # after messages = [ChatMessage(role=MessageRole.USER, ...), ChatMessage(role=MessageRole.ASSISTANT, ...), ChatMessage(role=MessageRole.USER, ...)] # last user turn present
Defensive patterns
Strategy: validation
Validate before calling
assert any(m.role == MessageRole.USER for m in request.messages), 'chat requires a user message'
Type guard
def has_user_message(messages: list[ChatMessage]) -> bool:
return any(m.role == MessageRole.USER for m in messages) Try / catch
try:
await chat_facade.create_chat_event_generator(request=request)
except Errors.InvalidRequest as e:
if 'No user message' in str(e):
request.messages.append(ChatMessage(role=MessageRole.USER, blocks=[TextBlock(text=user_query)])) Prevention
- Always append the current user turn before sending
- Build messages via a helper that guarantees a user role
- Reject empty conversation payloads at the client boundary
When it happens
Trigger: POST /v1/chat/completions with only assistant/system/tool messages; empty messages array; role string mismatch (e.g. 'user ' with whitespace or wrong enum) after manual payload construction.
Common situations: Client bugs that build the message list conditionally and skip the user turn; replaying conversation history without appending the new user query; serialization that drops the role field or maps it incorrectly.
Related errors
- Duplicate tool use ID found: {block.id}
- Tool result block references an unknown tool use ID: {block.
- Tool result blocks must match the tool use IDs in the same m
- System messages should be as layer in the context stack.
- TLDR blocks can only be used in assistant messages: {message
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/f753eb0cc9c08c8b.
Report an issue: GitHub.