dotnet/machinelearning · error · InvalidOperationException

Please provide a message with a valid role. The valid roles

Error message

Please provide a message with a valid role. The valid roles are System, User, and Assistant.

What it means

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.

Source

Thrown at src/Microsoft.ML.GenAI.Phi/Phi3/Phi3ChatTemplateBuilder.cs:35

namespace Microsoft.ML.GenAI.Phi;

public class Phi3ChatTemplateBuilder : IChatTemplateBuilder, IMEAIChatTemplateBuilder
{
    private const char Newline = '\n';

    public static Phi3ChatTemplateBuilder Instance { get; } = new Phi3ChatTemplateBuilder();

    public string BuildPrompt(IEnumerable<IMessage> messages, IEnumerable<FunctionContract>? tools = null)
    {
        var availableRoles = new[] { Role.System, Role.User, Role.Assistant };
        if (messages.Any(m => m.GetContent() is null))
        {
            throw new InvalidOperationException("Please provide a message with content.");
        }

        if (messages.Any(m => m.GetRole() is null || availableRoles.Contains(m.GetRole()!.Value) == false))
        {
            throw new InvalidOperationException("Please provide a message with a valid role. The valid roles are System, User, and Assistant.");
        }

        // construct template based on instruction from
        // https://huggingface.co/microsoft/Phi-3-mini-128k-instruct#chat-format

        var sb = new StringBuilder();
        foreach (var message in messages)
        {
            var role = message.GetRole()!.Value;
            var content = message.GetContent()!;
            sb.Append(message switch
            {
                _ when message.GetRole() == Role.System => $"<|system|>{Newline}{content}<|end|>{Newline}",
                _ when message.GetRole() == Role.User => $"<|user|>{Newline}{content}<|end|>{Newline}",
                _ when message.GetRole() == Role.Assistant => $"<|assistant|>{Newline}{content}<|end|>{Newline}",
                _ => throw new InvalidOperationException("Invalid role.")
            });
        }

View on GitHub (pinned to 7b76e69cf9)

Solutions

  1. Remove or convert tool/function-role messages to assistant or user messages before building the prompt.
  2. Flatten tool results into assistant messages with textual content.
  3. Validate roles in your pipeline before handing history to the template builder.

Example fix

// before
var prompt = builder.BuildPrompt(history); // history contains a Role.Tool message
// after
var prompt = builder.BuildPrompt(history.Where(m => m.GetRole() is Role.System or Role.User or Role.Assistant));
Defensive patterns

Strategy: validation

Validate before calling

var allowed = new[] { Role.System, Role.User, Role.Assistant };
if (messages.Any(m => m.GetRole() is null || !allowed.Contains(m.GetRole()!.Value)))
    throw new InvalidOperationException("History contains roles unsupported by Phi-3 (System/User/Assistant only).");

Type guard

bool IsPhi3Role(IMessage m) => m.GetRole() is Role.System or Role.User or Role.Assistant;

Try / catch

try { var prompt = builder.BuildPrompt(messages); } catch (InvalidOperationException ex) when (ex.Message.Contains("valid role")) { messages = messages.Where(IsPhi3Role); prompt = builder.BuildPrompt(messages); }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of dotnet/machinelearning@7b76e69cf9 (2026-09-11). Data as JSON: /api/errors/b260e6b6f7912ef3. Report an issue: GitHub.