microsoft/semantic-kernel · error · InvalidOperationException
Chat history can't contain only system messages.
Error message
Chat history can't contain only system messages.
What it means
Thrown by GeminiChatCompletionClient.ValidateChatHistory when every message in the ChatHistory has AuthorRole.System. Gemini requires at least one non-system message (a user or assistant turn) to generate a response. An all-system history has no actual conversation content for the model to respond to.
Source
Thrown at dotnet/src/Connectors/Connectors.Google/Core/Gemini/Clients/GeminiChatCompletionClient.cs:768
private static bool IsRequestableTool(IEnumerable<GeminiTool.FunctionDeclaration> functions, GeminiFunctionToolCall ftc)
=> functions.Any(geminiFunction =>
string.Equals(geminiFunction.Name, ftc.FullyQualifiedName, StringComparison.OrdinalIgnoreCase));
private static bool CheckAutoInvokeCondition(Kernel? kernel, GeminiPromptExecutionSettings geminiExecutionSettings)
{
bool autoInvoke = kernel is not null
&& geminiExecutionSettings.ToolCallBehavior?.MaximumAutoInvokeAttempts > 0
&& s_inflightAutoInvokes.Value < MaxInflightAutoInvokes;
ValidateAutoInvoke(autoInvoke, geminiExecutionSettings.CandidateCount ?? 1);
return autoInvoke;
}
private static void ValidateChatHistory(ChatHistory chatHistory)
{
Verify.NotNullOrEmpty(chatHistory);
if (chatHistory.All(message => message.Role == AuthorRole.System))
{
throw new InvalidOperationException("Chat history can't contain only system messages.");
}
}
private async IAsyncEnumerable<GeminiChatMessageContent> ProcessChatResponseStreamAsync(
Stream responseStream,
[EnumeratorCancellation] CancellationToken ct)
{
await foreach (var response in this.ParseResponseStreamAsync(responseStream, ct: ct).ConfigureAwait(false))
{
foreach (var messageContent in this.ProcessChatResponse(response))
{
yield return messageContent;
}
}
}
private async IAsyncEnumerable<GeminiResponse> ParseResponseStreamAsync(
Stream responseStream,View on GitHub (pinned to c028a0c7dc)
Solutions
- Add at least one user or assistant message to the chat history before calling the Gemini client.
- Move system instructions to GeminiPromptExecutionSettings.SystemInstruction instead of using AuthorRole.System messages.
- Validate the history before the call: if (history.All(m => m.Role == AuthorRole.System)) throw new InvalidOperationException("Add a user message.");
Example fix
// before — only system messages
history.AddSystemMessage("You are a translator.");
await client.GetChatMessageContentsAsync(history);
// after — add a user message
history.AddSystemMessage("You are a translator.");
history.AddUserMessage("Translate 'hello' to French.");
await client.GetChatMessageContentsAsync(history); Defensive patterns
Strategy: validation
Validate before calling
if (chatHistory.All(m => m.Role == AuthorRole.System))
{
throw new InvalidOperationException(
"Chat history must contain at least one non-system message for Gemini.");
}
// or auto-fix by adding a placeholder user message
if (chatHistory.Count > 0 && chatHistory.All(m => m.Role == AuthorRole.System))
{
chatHistory.AddUserMessage("(continue)");
} Type guard
static bool HasNonSystemMessage(ChatHistory history) =>
history.Any(m => m.Role != AuthorRole.System); Try / catch
try { var result = await client.GetChatMessageContentsAsync(history); }
catch (InvalidOperationException ex) when (ex.Message.Contains("only system messages"))
{
logger.LogWarning("Chat history had only system messages; adding user prompt.");
history.AddUserMessage(userInput);
result = await client.GetChatMessageContentsAsync(history);
} Prevention
- Always include at least one user message in the chat history before calling Gemini.
- Use GeminiPromptExecutionSettings.SystemInstruction for system prompts instead of System-role messages.
- Validate chat history structure before sending to any LLM connector.
When it happens
Trigger: Calling GetChatMessageContentsAsync (or the streaming variant) with a ChatHistory where chatHistory.All(m => m.Role == AuthorRole.System) returns true. This means zero non-system messages exist. An empty history is caught earlier by Verify.NotNullOrEmpty.
Common situations: Building a chat history programmatically and only adding system instructions. Copying a history from another connector that stores everything as system messages. Forgetting to append the actual user query after setting up the system prompt.
Related errors
- MaxTokens {maxTokens} is not valid, the value must be greate
- Gemini API doesn't support author role: {value}
- Auto-invocation of tool calls may only be used with a {nameo
- GeminiPart is invalid. One and only one property among Text,
- Image content does not contain any data or uri.
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/3bf9e26add901737.
Report an issue: GitHub.