{"record":{"id":"560d2560f0fe8ff6","repo":"dotnet/machinelearning","slug":"invalid-role-560d25","errorCode":null,"errorMessage":"Invalid role.","messagePattern":"Invalid role\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.ML.GenAI.Phi/Phi3/Phi3ChatTemplateBuilder.cs","lineNumber":51,"sourceCode":"        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        }\n\n        sb.Append(\"<|assistant|>\");\n        var input = sb.ToString();\n\n        return input;\n    }\n\n    public string BuildPrompt(ChatHistory chatHistory)\n    {\n        // build prompt from chat history\n        var sb = new StringBuilder();\n\n        foreach (var message in chatHistory)\n        {\n            foreach (var item in message.Items)\n            {","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/dotnet/machinelearning/blob/7b76e69cf964daeca3f1377af6bc5543284d56c6/src/Microsoft.ML.GenAI.Phi/Phi3/Phi3ChatTemplateBuilder.cs#L33-L69","documentation":"As a final defensive switch arm in the IMessage overload of BuildPrompt, any message whose role is not System, User, or Assistant hits the discard case and throws InvalidOperationException('Invalid role.'). This normally cannot be reached because the role check earlier in the method already filters messages.","triggerScenarios":"Only reachable if the earlier role-validation Any(...) check passed but the per-message switch still encounters an unexpected role — practically it guards against internal inconsistency or race-modified message state.","commonSituations":"Rare in practice; may indicate custom Role values that compare inconsistently, or mutable messages whose role changed between validation and rendering.","solutions":["Ensure messages are immutable/not mutated between validation and prompt building.","Sanitize roles to System/User/Assistant before calling BuildPrompt.","If you can hit this consistently, report it — the earlier validation should have caught the bad role."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Reuse the same role check the builder performs internally\nif (messages.Any(m => m.GetRole() is not (Role.System or Role.User or Role.Assistant)))\n    throw new InvalidOperationException(\"Unsupported role detected.\");","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 == \"Invalid role.\") { logger.LogError(ex, \"Role changed between validation and rendering\"); throw; }","preventionTips":["Treat IMessage objects as immutable once queued.","Avoid custom Role values when targeting Phi-3.","Re-check roles right before building the prompt if history is shared across threads."],"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"}