microsoft/semantic-kernel · error · KernelException

Message must have a role.

Error message

Message must have a role.

What it means

Thrown by the Handlebars `message` helper registered in KernelSystemHelpers when the helper's first argument (a hash/object) does not contain a `role` key. Semantic Kernel uses this helper so prompt authors can emit role-tagged chat segments inside a Handlebars template; the role is mandatory because the rendered pseudo-tags `{{role}}~` / `{{/role}}~` drive downstream chat-message parsing.

Source

Thrown at dotnet/src/Extensions/PromptTemplates.Handlebars/Helpers/KernelHelpers/KernelSystemHelpers.cs:60

    /// <param name="handlebarsInstance">The <see cref="IHandlebars"/>-instance.</param>
    /// <param name="kernel">Kernel instance.</param>
    /// <param name="variables">Dictionary of variables maintained by the Handlebars context.</param>
    /// <exception cref="KernelException">Exception thrown when a message does not contain a defining role.</exception>
    private static void RegisterSystemHelpers(
        IHandlebars handlebarsInstance,
        Kernel kernel,
        KernelArguments variables)
    {
        // TODO [@teresaqhoang]: Issue #3947 Isolate Handlebars Kernel System helpers in their own class
        // Should also consider standardizing the naming conventions for these helpers, i.e., 'Message' instead of 'message'
        handlebarsInstance.RegisterHelper("message", static (writer, options, context, arguments) =>
        {
            var parameters = (IDictionary<string, object>)arguments[0];

            // Verify that the message has a role
            if (!parameters!.TryGetValue("role", out object? value))
            {
                throw new KernelException("Message must have a role.");
            }

            writer.Write($"<{value}~>", false);
            options.Template(writer, context);
            writer.Write($"</{value}~>", false);
        });

        handlebarsInstance.RegisterHelper("set", (writer, context, arguments) =>
        {
            var name = string.Empty;
            object? value = string.Empty;
            if (arguments[0].GetType() == typeof(HashParameterDictionary))
            {
                // Get the parameters from the template arguments
                var parameters = (IDictionary<string, object>)arguments[0];
                name = (string)parameters!["name"];
                value = GetArgumentValue(parameters!["value"], variables);
            }

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Add an explicit role to the helper call, e.g. `{{message role="user"}}...{{/message}}`.
  2. Inspect the object you pass as the first argument and ensure it contains a `role` key with a non-null value.
  3. Check for casing typos — the key must be exactly `role` (not `Role` or `userRole`).

Example fix

// before
{{#message}}hello{{/message}}
// after
{{#message role="user"}}hello{{/message}}
Defensive patterns

Strategy: validation

Validate before calling

// Before render, ensure each message-helper call site supplies a role.
// In code-built arguments, validate the object you pass:
static bool HasRole(IDictionary<string,object> p) => p.ContainsKey("role");

Prevention

When it happens

Trigger: Calling `{{message}}` without a `role`, e.g. `{{message content="hi"}}` or `{{message this}}` where the object has no `role` property. Any invocation where `arguments[0]` is an IDictionary whose TryGetValue("role") returns false.

Common situations: Authoring chat-history or system/user/assistant message blocks in a `.handlebars` prompt template and forgetting the role attribute; passing a context object that was built dynamically and the role field was conditionally omitted or mis-cased.

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/7b13e8712aa20d72. Report an issue: GitHub.