{"record":{"id":"b260e6b6f7912ef3","repo":"dotnet/machinelearning","slug":"please-provide-a-message-with-a-valid-role-the-va-b260e6","errorCode":null,"errorMessage":"Please provide a message with a valid role. The valid roles are System, User, and Assistant.","messagePattern":"Please provide a message with a valid role\\. The valid roles are System, User, and Assistant\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.ML.GenAI.Phi/Phi3/Phi3ChatTemplateBuilder.cs","lineNumber":35,"sourceCode":"namespace Microsoft.ML.GenAI.Phi;\n\npublic class Phi3ChatTemplateBuilder : IChatTemplateBuilder, IMEAIChatTemplateBuilder\n{\n    private const char Newline = '\\n';\n\n    public static Phi3ChatTemplateBuilder Instance { get; } = new Phi3ChatTemplateBuilder();\n\n    public string BuildPrompt(IEnumerable<IMessage> messages, IEnumerable<FunctionContract>? tools = null)\n    {\n        var availableRoles = new[] { Role.System, Role.User, Role.Assistant };\n        if (messages.Any(m => m.GetContent() is null))\n        {\n            throw new InvalidOperationException(\"Please provide a message with content.\");\n        }\n\n        if (messages.Any(m => m.GetRole() is null || availableRoles.Contains(m.GetRole()!.Value) == false))\n        {\n            throw new InvalidOperationException(\"Please provide a message with a valid role. The valid roles are System, User, and Assistant.\");\n        }\n\n        // construct template based on instruction from\n        // https://huggingface.co/microsoft/Phi-3-mini-128k-instruct#chat-format\n\n        var sb = new StringBuilder();\n        foreach (var message in messages)\n        {\n            var role = message.GetRole()!.Value;\n            var content = message.GetContent()!;\n            sb.Append(message switch\n            {\n                _ when message.GetRole() == Role.System => $\"<|system|>{Newline}{content}<|end|>{Newline}\",\n                _ when message.GetRole() == Role.User => $\"<|user|>{Newline}{content}<|end|>{Newline}\",\n                _ when message.GetRole() == Role.Assistant => $\"<|assistant|>{Newline}{content}<|end|>{Newline}\",\n                _ => throw new InvalidOperationException(\"Invalid role.\")\n            });\n        }","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/dotnet/machinelearning/blob/7b76e69cf964daeca3f1377af6bc5543284d56c6/src/Microsoft.ML.GenAI.Phi/Phi3/Phi3ChatTemplateBuilder.cs#L17-L53","documentation":"BuildPrompt(IEnumerable<IMessage>) restricts messages to Role.System, Role.User, and Role.Assistant, matching the Phi-3 chat format. A message with a null role or any other role (e.g. Tool, Function) causes this InvalidOperationException.","triggerScenarios":"Calling BuildPrompt with a message whose GetRole() returns null, or whose role is not one of System/User/Assistant — typically tool/function-role messages passed into the template builder.","commonSituations":"Including AutoGen tool-call or tool-result messages in the history; migrating from another chat library whose roles don't map to Phi-3's three roles; role strings that failed to parse into the Role enum.","solutions":["Remove or convert tool/function-role messages to assistant or user messages before building the prompt.","Flatten tool results into assistant messages with textual content.","Validate roles in your pipeline before handing history to the template builder."],"exampleFix":"// before\nvar prompt = builder.BuildPrompt(history); // history contains a Role.Tool message\n// after\nvar prompt = builder.BuildPrompt(history.Where(m => m.GetRole() is Role.System or Role.User or Role.Assistant));","handlingStrategy":"validation","validationCode":"var allowed = new[] { Role.System, Role.User, Role.Assistant };\nif (messages.Any(m => m.GetRole() is null || !allowed.Contains(m.GetRole()!.Value)))\n    throw new InvalidOperationException(\"History contains roles unsupported by Phi-3 (System/User/Assistant only).\");","typeGuard":"bool IsPhi3Role(IMessage m) => m.GetRole() is Role.System or Role.User or Role.Assistant;","tryCatchPattern":"try { var prompt = builder.BuildPrompt(messages); } catch (InvalidOperationException ex) when (ex.Message.Contains(\"valid role\")) { messages = messages.Where(IsPhi3Role); prompt = builder.BuildPrompt(messages); }","preventionTips":["Convert tool/function messages to assistant text before adding them to history.","Centralize role mapping in one adapter function.","Pin down message creation so roles can only be the three supported values."],"tags":["csharp","llm","chat-template","roles"],"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-14T05:17:10.506Z"}