dotnet/machinelearning · error · InvalidOperationException

Please provide a message with content.

Error message

Please provide a message with content.

What it means

The Llama 3.1 chat template builder requires every message to carry non-null content, because the LLaMA prompt format embeds each message's text between header/eot tags. It throws InvalidOperationException when any IMessage in the sequence returns null from GetContent().

Solutions

  1. Filter or skip messages with null content before calling BuildPrompt.
  2. Replace null content with an empty string or a descriptive placeholder if the message must be kept.
  3. Ensure upstream message-construction code always sets non-null content.
  4. Convert non-text messages (tool calls, images) to their text representation before building the prompt.

Example fix

// before
var prompt = builder.BuildPrompt(messages);
// after
var prompt = builder.BuildPrompt(messages.Where(m => m.GetContent() is not null));
Defensive patterns

Strategy: validation

Validate before calling

if (messages.Any(m => m.GetContent() is null))
    throw new InvalidOperationException("All messages must have non-null content before BuildPrompt.");

Type guard

static bool HasContent(IMessage m) => m.GetContent() is not null;
var safe = messages.Where(HasContent);

Try / catch

try { prompt = builder.BuildPrompt(messages); }
catch (InvalidOperationException ex) when (ex.Message.Contains("content"))
{ prompt = builder.BuildPrompt(messages.Where(m => m.GetContent() is not null)); }

Prevention

When it happens

Trigger: Calling BuildPrompt(IEnumerable<IMessage>, ...) with a message whose GetContent() is null — e.g. a message constructed without content, a tool/function-call message whose payload is not text, or a message deserialized with a missing content field.

Common situations: Feeding tool-call or image messages into the text-only LLaMA template; constructing TextMessage with a null body from an upstream model response; pipeline stages that emit role-only placeholder messages.

Related errors


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

Appendix: source

Thrown at src/Microsoft.ML.GenAI.LLaMA/Llama3_1ChatTemplateBuilder.cs:25

using Microsoft.Extensions.AI;
using Microsoft.ML.GenAI.Core;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using TextContent = Microsoft.SemanticKernel.TextContent;

namespace Microsoft.ML.GenAI.LLaMA;
#pragma warning disable MSML_GeneralName // This name should be PascalCased
public class Llama3_1ChatTemplateBuilder : IChatTemplateBuilder, IMEAIChatTemplateBuilder
#pragma warning restore MSML_GeneralName // This name should be PascalCased
{
    private const char Newline = '\n';

    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://github.com/meta-llama/llama3/blob/11817d47e1ba7a4959b025eb1ca308572e0e3963/llama/generation.py#L280

        var sb = new StringBuilder();
        sb.Append("<|begin_of_text|>");
        foreach (var message in messages)
        {
            var role = message.GetRole()!.Value;
            var content = message.GetContent()!;
            sb.Append(message switch
            {

View on GitHub (pinned to 7b76e69cf9)