dotnet/machinelearning · error · InvalidOperationException

Please provide a message with content.

Error message

Please provide a message with content.

What it means

Phi3ChatTemplateBuilder.BuildPrompt(IEnumerable<IMessage>) validates that every message has non-null content before rendering the Phi-3 chat template. Any message whose GetContent() is null makes the prompt impossible to render, so it throws InvalidOperationException.

Source

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

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

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}",

View on GitHub (pinned to 7b76e69cf9)

Solutions

  1. Filter out messages with null content before calling BuildPrompt.
  2. Replace null-content messages with a placeholder string such as string.Empty or '[no content]'.
  3. Fix upstream message construction/deserialization so Content is always populated.

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

bool allHaveContent = messages.All(m => m.GetContent() is not null);
if (!allHaveContent) throw new InvalidOperationException("All messages must have content before building the Phi-3 prompt.");

Type guard

bool HasContent(IMessage m) => m.GetContent() is not null;

Try / catch

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

Prevention

When it happens

Trigger: Passing a message collection to BuildPrompt where at least one IMessage has null content — e.g. a message constructed from a null string, a tool-call-only message, or deserialization that left Content unset.

Common situations: Binding chat history from external JSON where 'content' was absent; creating TextMessage with a null body programmatically; including function-call result messages that carry no textual content.

Related errors


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