dotnet/machinelearning · error · NotSupportedException
Unsupported role {message.Role}
Error message
Unsupported role {message.Role} What it means
The LLaMA 3.1 template only renders messages authored as System, User, or Assistant. BuildPrompt throws NotSupportedException when a ChatMessage's Role is anything else (e.g. Tool), because the header format has no corresponding tag.
Source
Thrown at src/Microsoft.ML.GenAI.LLaMA/Llama3_1ChatTemplateBuilder.cs:79
sb.Append("<|begin_of_text|>");
foreach (var message in chatHistory)
{
foreach (var item in message.Items)
{
if (item is not TextContent textContent)
{
throw new NotSupportedException($"Only text content is supported, but got {item.GetType().Name}");
}
var text = textContent.Text?.Trim() ?? string.Empty;
var prompt = message.Role switch
{
_ when message.Role == AuthorRole.System => $"<|start_header_id|>system<|end_header_id|>{Newline}{text}<|eot_id|>{Newline}",
_ when message.Role == AuthorRole.User => $"<|start_header_id|>user<|end_header_id|>{Newline}{text}<|eot_id|>{Newline}",
_ when message.Role == AuthorRole.Assistant => $"<|start_header_id|>assistant<|end_header_id|>{Newline}{text}<|eot_id|>{Newline}",
_ => throw new NotSupportedException($"Unsupported role {message.Role}")
};
sb.Append(prompt);
}
}
sb.Append($"<|start_header_id|>assistant<|end_header_id|>{Newline}");
return sb.ToString();
}
public string BuildPrompt(IEnumerable<ChatMessage> messages, ChatOptions? options = null, bool appendAssistantTag = true)
{
var availableRoles = new[] { ChatRole.System, ChatRole.User, ChatRole.Assistant };
if (messages.Any(m => m.Text is null))
{
throw new InvalidOperationException("Please provide a message with content.");
}View on GitHub (pinned to 7b76e69cf9)
Solutions
- Convert Tool-role messages to User or Assistant text messages before prompt building.
- Remove Tool-role messages from the history passed to BuildPrompt.
- Explicitly set AuthorRole on every message to System, User, or Assistant.
- Merge tool output into the preceding assistant or a new user message.
Example fix
// before
var prompt = builder.BuildPrompt(history); // contains AuthorRole.Tool
// after
var mapped = history.Select(m => m.Role == AuthorRole.Tool
? new ChatMessage(AuthorRole.User, m.Content)
: m);
var prompt = builder.BuildPrompt(mapped); Defensive patterns
Strategy: validation
Validate before calling
var valid = new[] { AuthorRole.System, AuthorRole.User, AuthorRole.Assistant };
if (chatHistory.Any(m => !valid.Contains(m.Role)))
throw new InvalidOperationException("Unsupported AuthorRole in history for LLaMA template."); Type guard
static bool HasSupportedRole(ChatMessage m) =>
m.Role == AuthorRole.System || m.Role == AuthorRole.User || m.Role == AuthorRole.Assistant; Try / catch
try { prompt = builder.BuildPrompt(chatHistory); }
catch (NotSupportedException ex) when (ex.Message.StartsWith("Unsupported role"))
{ prompt = builder.BuildPrompt(chatHistory.Where(HasSupportedRole)); } Prevention
- Convert Tool-role messages to User/Assistant text messages before prompt building.
- Filter unsupported roles at the chat-history boundary.
- Test with histories produced by function-calling pipelines, not just handcrafted ones.
When it happens
Trigger: Calling BuildPrompt(IEnumerable<ChatMessage>, ...) with a ChatMessage whose AuthorRole is Tool/Function or any custom role — typical when function-calling results are kept verbatim in chat history.
Common situations: Semantic Kernel function-calling pipelines adding Tool-role messages; converting histories from other providers with extra roles; custom AuthorRole values.
Related errors
- Please provide a message with a valid role. The valid roles
- Invalid role.
- Only text content is supported, but got {item.GetType().Name
- Please provide a message with content.
- Failed to generate a reply.
AI-assisted analysis of dotnet/machinelearning@7b76e69cf9 (2026-09-11).
Data as JSON: /api/errors/e4f5b831f75d2d9c.
Report an issue: GitHub.