{"record":{"id":"c149a761c1926a31","repo":"zylon-ai/private-gpt","slug":"last-message-role-must-be-one-of-self-valid-last","errorCode":null,"errorMessage":"Last message role must be one of {self._valid_last_message_roles}, but got {self.messages[-1].role}","messagePattern":"Last message role must be one of (.+?), but got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":422,"severity":"error","filePath":"private_gpt/server/chat/chat_models.py","lineNumber":324,"sourceCode":"        if self.seed and self.seed < 0:\n            self.seed = None\n\n        if self.max_tokens is not None and self.max_tokens <= 0:\n            self.max_tokens = None\n\n        if not self.messages:\n            raise ValueError(\"Messages cannot be empty\")\n\n        for message in self.messages:\n            if not message.content:\n                raise ValueError(f\"Message content cannot be empty: {message}\")\n            if isinstance(message.content, list):\n                for block in message.content:\n                    if block is None:\n                        raise ValueError(f\"Block cannot be None: {message}\")\n\n        if self.messages[-1].role not in self._valid_last_message_roles:\n            raise ValueError(\n                f\"Last message role must be one of {self._valid_last_message_roles}, but got {self.messages[-1].role}\"\n            )\n\n        # Check tools and tool choice\n        if self.tools and self.tool_choice and self.tool_choice.type == \"tool\":\n            if not self.tools:\n                raise ValueError(\"Tool choice is set, but no tools are provided.\")\n            if self.tool_choice.name not in [tool.name for tool in self.tools]:\n                raise ValueError(\n                    f\"Tool choice '{self.tool_choice}' is not in the provided tools.\"\n                )\n\n        if not self.tools and self.tool_context:\n            raise ValueError(\n                \"Tool context is provided, but no tools are specified. \"\n                \"Please provide tools to use with the tool context.\"\n            )\n","sourceCodeStart":306,"sourceCodeEnd":342,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/private_gpt/server/chat/chat_models.py#L306-L342","documentation":"ChatBody requires the final message role to be 'user' or 'assistant' (see _valid_last_message_roles). Ending on a 'system' or 'tool' message is rejected because the LLM call needs the last turn to be one the model responds to. This mirrors OpenAI-compatible API behavior enforced server-side via the model_validator.","triggerScenarios":"Sending a conversation that ends with {\"role\":\"system\", ...} (system prompt appended last) or a trailing tool-result message not followed by a user/assistant turn; request builders that concatenate system messages at the end of history.","commonSituations":"Prompt templates that append instructions after the user message; multi-step tool loops that send the tool output as the final message without an assistant acknowledgement; role typos like 'Tool' or 'USER'.","solutions":["Reorder so all system messages come first and the conversation ends with the user's (or assistant's) message.","In tool loops, append a final user nudge (e.g. 'continue') after tool results, or ensure the assistant turn closes the loop.","Check for role casing/typos — roles must be exactly 'user' or 'assistant'."],"exampleFix":"# before\nmessages=[{\"role\":\"user\",\"content\":\"hi\"},{\"role\":\"system\",\"content\":\"be brief\"}]\n\n# after\nmessages=[{\"role\":\"system\",\"content\":\"be brief\"},{\"role\":\"user\",\"content\":\"hi\"}]","handlingStrategy":"validation","validationCode":"assert messages[-1]['role'] in ('user', 'assistant'), 'conversation must end with user/assistant'","typeGuard":"def ends_with_valid_role(msgs: list[dict]) -> bool:\n    return msgs and msgs[-1].get('role') in {'user', 'assistant'}","tryCatchPattern":"except ValidationError as e:\n    if 'Last message role' in str(e):\n        messages = [m for m in messages if m['role'] != 'system'] + system_first(messages)\n        retry()\n    else:\n        raise","preventionTips":["Put system messages first in request builders","Close tool loops with an assistant or user turn","Validate role ordering client-side before each send"],"tags":["validation","chat-api","message-roles","ordering"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}