{"record":{"id":"f753eb0cc9c08c8b","repo":"zylon-ai/private-gpt","slug":"no-user-message-found-in-request","errorCode":null,"errorMessage":"No user message found in request.","messagePattern":"No user message found in request\\.","errorType":"http","errorClass":"Errors.InvalidRequest","httpStatus":400,"severity":"error","filePath":"private_gpt/server/chat/interceptors/validator_request_interceptor.py","lineNumber":183,"sourceCode":"        return\n\n    @staticmethod\n    def _extract_text(message: ChatMessage) -> str:\n        \"\"\"Extract normalized text from message blocks.\"\"\"\n        parts = [\n            block.text.strip()\n            for block in message.blocks\n            if isinstance(block, TextBlock) and block.text and block.text.strip()\n        ]\n        return \"\\n\".join(parts)\n\n    @staticmethod\n    def _last_user_message(messages: list[ChatMessage]) -> ChatMessage:\n        \"\"\"Return the latest user message from request messages.\"\"\"\n        for message in reversed(messages):\n            if message.role == MessageRole.USER:\n                return message\n        raise Errors.InvalidRequest(\"No user message found in request.\")\n\n    @staticmethod\n    def _system_message_text(messages: list[ChatMessage]) -> str | None:\n        \"\"\"Return first system message text when present.\"\"\"\n        for message in messages:\n            if message.role != MessageRole.SYSTEM:\n                continue\n            text = ValidatorRequestInterceptor._extract_text(message)\n            if text:\n                return text\n        return None\n","sourceCodeStart":165,"sourceCodeEnd":195,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/private_gpt/server/chat/interceptors/validator_request_interceptor.py#L165-L195","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nmessages = [ChatMessage(role=MessageRole.ASSISTANT, ...)]\n\n# after\nmessages = [ChatMessage(role=MessageRole.USER, ...), ChatMessage(role=MessageRole.ASSISTANT, ...), ChatMessage(role=MessageRole.USER, ...)]  # last user turn present","handlingStrategy":"validation","validationCode":"assert any(m.role == MessageRole.USER for m in request.messages), 'chat requires a user message'","typeGuard":"def has_user_message(messages: list[ChatMessage]) -> bool:\n    return any(m.role == MessageRole.USER for m in messages)","tryCatchPattern":"try:\n    await chat_facade.create_chat_event_generator(request=request)\nexcept Errors.InvalidRequest as e:\n    if 'No user message' in str(e):\n        request.messages.append(ChatMessage(role=MessageRole.USER, blocks=[TextBlock(text=user_query)]))","preventionTips":["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"],"tags":["validation","chat","messages","misuse"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}