microsoft/semantic-kernel · error · ArgumentException
Role must be one of: system, user, assistant or tool. {role}
Error message
Role must be one of: system, user, assistant or tool. {role} is an invalid role. What it means
Thrown by the MistralChatMessage JSON constructor when the supplied role is non-null but not one of the allowed values: system, user, assistant, or tool. The constructor validates the role because the Mistral chat completions API only accepts those four roles. The offending value is interpolated into the message and reported as an ArgumentException on the 'role' parameter.
Source
Thrown at dotnet/src/Connectors/Connectors.MistralAI/Client/MistralChatMessage.cs:43
[JsonPropertyName("tool_call_id")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? ToolCallId { get; set; }
[JsonPropertyName("tool_calls")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public IList<MistralToolCall>? ToolCalls { get; set; }
/// <summary>
/// Construct an instance of <see cref="MistralChatMessage"/>.
/// </summary>
/// <param name="role">If provided must be one of: system, user, assistant</param>
/// <param name="content">Content of the chat message</param>
[JsonConstructor]
internal MistralChatMessage(string? role, object? content)
{
if (role is not null and not "system" and not "user" and not "assistant" and not "tool")
{
throw new System.ArgumentException($"Role must be one of: system, user, assistant or tool. {role} is an invalid role.", nameof(role));
}
this.Role = role;
this.Content = content;
}
}
View on GitHub (pinned to c028a0c7dc)
Solutions
- Map foreign roles to Mistral roles before constructing: 'model'/'chatbot' -> 'assistant', 'function' -> 'tool', 'developer'/'system' -> 'system'.
- Ensure deserialized chat history uses only system/user/assistant/tool roles.
- If building messages manually, pass role as null or one of the four valid strings.
Example fix
// before var msg = new MistralChatMessage(role: "model", content: "hi"); // throws // after var msg = new MistralChatMessage(role: "assistant", content: "hi");
Defensive patterns
Strategy: type-guard
Validate before calling
static readonly HashSet<string> ValidRoles = new() { "system", "user", "assistant", "tool" };
static string NormalizeRole(string? r) => r switch
{
null => "user",
"model" or "chatbot" or "bot" => "assistant",
"function" => "tool",
"developer" => "system",
_ => ValidRoles.Contains(r) ? r : "user"
}; Type guard
static bool IsValidMistralRole(string? r)
=> r is null || r is "system" or "user" or "assistant" or "tool"; Try / catch
try { var msg = new MistralChatMessage(role, content); }
catch (ArgumentException ex) when (ex.Message.Contains("is an invalid role"))
{ logger.LogError(ex, "Translate the role to one of system/user/assistant/tool."); throw; } Prevention
- Map foreign roles (model->assistant, function->tool) before constructing MistralChatMessage.
- Validate roles with the type guard when importing chat histories.
- Pass role as null to default rather than guessing.
When it happens
Trigger: Deserializing a MistralChatMessage from JSON whose role is e.g. 'function', 'developer', 'model', 'chatbot', or any custom string; or constructing a MistralChatMessage manually with an unsupported role.
Common situations: Mapping chat histories from another provider (OpenAI 'function'/'tool', Gemini 'model', custom 'bot') into Mistral messages without translating the role; schema drift where a new role string appears; typos in role strings.
Related errors
- Bedrock agents must be invoked with a user message
- Invalid role: {authorRole}
- Chat completions not found
- Invalid choice
- Invalid kernel selection. {selectedKernelName} is not a vali
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/10629cf463d46637.
Report an issue: GitHub.