{"record":{"id":"092a4ad5baa88fc1","repo":"dotnet/machinelearning","slug":"invalid-role-092a4a","errorCode":null,"errorMessage":"Invalid role.","messagePattern":"Invalid role\\.","errorType":"validation","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.ML.GenAI.Mistral/Mistral_7B_0_3ChatTemplateBuilder.cs","lineNumber":60,"sourceCode":"\n        var sb = new StringBuilder();\n        foreach (var message in firstSequence)\n        {\n            // skip system\n            if (message.GetRole() == Role.System)\n            {\n                continue;\n            }\n\n            var content = message.GetContent()!;\n            sb.Append(message switch\n            {\n                ToolCallMessage toolCallMessage => BuildFromToolCallMessage(toolCallMessage),\n                ToolCallResultMessage toolCallResultMessage => BuildFromToolCallResultMessage(toolCallResultMessage),\n                ToolCallAggregateMessage toolCallAggregateMessage => BuildFromAggregrateToolCallMessage(toolCallAggregateMessage),\n                TextMessage when message.GetRole() == Role.User => $\"[INST]{content.Trim()}[/INST]\",\n                TextMessage when message.GetRole() == Role.Assistant => $\"{content.Trim()}</s>\",\n                _ => throw new InvalidOperationException(\"Invalid role.\")\n            });\n        }\n\n        // insert [AVAILABLE TOOLS] section if tools are provided\n        if (tools?.Any() == true)\n        {\n            var schemas = tools.Select(t => new\n            {\n                type = \"function\",\n                function = new\n                {\n                    name = t.Name,\n                    description = t.Description,\n                    parameters = BuildJsonSchemaFromFunctionContract(t)\n                }\n            });\n            var schemaPrompt = JsonSerializer.Serialize(schemas);\n","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/dotnet/machinelearning/blob/7b76e69cf964daeca3f1377af6bc5543284d56c6/src/Microsoft.ML.GenAI.Mistral/Mistral_7B_0_3ChatTemplateBuilder.cs#L42-L78","documentation":"Mistral_7B_0_3ChatTemplateBuilder.BuildPrompt formats each message in a ChatHistory into Mistral's [INST]...[/INST] template. Only ToolCall/ToolResult/aggregate messages and TextMessage with Role.User or Role.Assistant are supported; any other role (e.g. System passed as a TextMessage after the first message, or a custom role) falls into the default arm and throws InvalidOperationException.","triggerScenarios":"Calling BuildPrompt(IEnumerable<Message> chatHistory, ...) with a TextMessage whose GetRole() is neither User nor Assistant — most commonly a System TextMessage appearing after position 0, or a message whose role string was parsed as something unexpected.","commonSituations":"Porting code from another chat template (e.g. Llama) that accepts a System role anywhere in the history; hand-built ChatHistory with a system-style TextMessage; a new Role enum value added upstream but not handled by the Mistral template.","solutions":["Ensure all messages are TextMessage with Role.User or Role.Assistant (or tool-call message types)","Fold system instructions into the first user message instead of a separate System TextMessage","Add a pattern arm for the extra role you need, e.g. TextMessage when message.GetRole() == Role.System => system-style formatting","Catch InvalidOperationException around BuildPrompt and log message.GetRole() to identify the offending message"],"exampleFix":"// before\nnew TextMessage(Role.System, \"You are helpful.\")\n// after\nnew TextMessage(Role.User, \"You are helpful.\\n\\n\" + userContent)","handlingStrategy":"validation","validationCode":"bool allRolesValid = messages.OfType<TextMessage>().All(m => m.GetRole() == Role.User || m.GetRole() == Role.Assistant);\nif (!allRolesValid) throw new ArgumentException(\"Mistral template supports only User/Assistant text messages\");","typeGuard":"static bool IsSupportedMessage(Message m) =>\n    m is ToolCallMessage or ToolCallResultMessage or ToolCallAggregateMessage ||\n    (m is TextMessage t && (t.GetRole() == Role.User || t.GetRole() == Role.Assistant));","tryCatchPattern":"try { prompt = builder.BuildPrompt(messages, systemMessage, tools); }\ncatch (InvalidOperationException ex) { log.Error(\"Unsupported role in chat history\", ex); throw; }","preventionTips":["Map roles to User/Assistant before templating","Fold system prompts into the first user message","Unit-test prompt building with every role your app emits"],"tags":["csharp","chat-template","invalid-role"],"backgroundTag":"invalid-enum-value","analyzedSha":"7b76e69cf964daeca3f1377af6bc5543284d56c6","analyzedAt":"2026-09-11T12:35:38.930Z","contentChangedAt":"2026-09-11T12:35:38.930Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}