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
- Add an explicit role to the helper call, e.g. `{{message role="user"}}...{{/message}}`.
- Inspect the object you pass as the first argument and ensure it contains a `role` key with a non-null value.
- 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
- Always author `{{message role="..."}}` with an explicit role attribute.
- If passing a context object, assert it has a `role` key in a unit test for your templates.
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
- `json` helper requires a value to be passed in.
- Invalid template format: {config.TemplateFormat}
- Configuration not found, please setup the notebooks first us
- AZURE_OPENAI_ENDPOINT is not set.
- AZURE_OPENAI_ENDPOINT is not set.
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/7b13e8712aa20d72.
Report an issue: GitHub.