microsoft/semantic-kernel · error · ArgumentException

MaxTokens {maxTokens} is not valid, the value must be greate

Error message

MaxTokens {maxTokens} is not valid, the value must be greater than zero

What it means

Thrown by ClientBase.ValidateMaxTokens when the maxTokens value is a non-null integer less than 1. The pattern match 'maxTokens is < 1' only fires for non-null values (nullable int), so null is explicitly allowed and means 'use the model default'. A value of 0 or any negative integer is rejected because it is meaningless as a token budget.

Source

Thrown at dotnet/src/Connectors/Connectors.Google/Core/ClientBase.cs:51

    protected ClientBase(
        HttpClient httpClient,
        ILogger? logger,
        string? apiKey = null)
    {
        Verify.NotNull(httpClient);

        this.HttpClient = httpClient;
        this.Logger = logger ?? NullLogger.Instance;
        this._apiKey = apiKey;
    }

    protected static void ValidateMaxTokens(int? maxTokens)
    {
        // If maxTokens is null, it means that the user wants to use the default model value
        if (maxTokens is < 1)
        {
            throw new ArgumentException($"MaxTokens {maxTokens} is not valid, the value must be greater than zero");
        }
    }

    protected async Task<string> SendRequestAndGetStringBodyAsync(
        HttpRequestMessage httpRequestMessage,
        CancellationToken cancellationToken)
    {
        using var response = await this.HttpClient.SendWithSuccessCheckAsync(httpRequestMessage, cancellationToken)
            .ConfigureAwait(false);
        var body = await response.Content.ReadAsStringWithExceptionMappingAsync(cancellationToken)
            .ConfigureAwait(false);
        return body;
    }

    protected async Task<HttpResponseMessage> SendRequestAndGetResponseImmediatelyAfterHeadersReadAsync(
        HttpRequestMessage httpRequestMessage,
        CancellationToken cancellationToken)
    {

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Set MaxTokens to null instead of 0 to use the model's default maximum output: settings.MaxTokens = null;
  2. Ensure any computed token budget is clamped to at least 1: settings.MaxTokens = Math.Max(1, budget);
  3. Validate before the call: if (settings.MaxTokens is int v && v < 1) throw or set to null.
  4. Do not use 0 as a sentinel for 'no limit' — use null.

Example fix

// before — 0 means 'no limit' in caller logic but throws
settings.MaxTokens = remainingTokens; // remainingTokens can be 0

// after — null means 'use model default'
settings.MaxTokens = remainingTokens > 0 ? remainingTokens : null;
Defensive patterns

Strategy: validation

Validate before calling

if (settings.MaxTokens is int tokens && tokens < 1)
{
    throw new ArgumentOutOfRangeException(nameof(settings.MaxTokens),
        $"MaxTokens must be >= 1 or null (for model default). Got {tokens}.");
}
// or normalize:
settings.MaxTokens = settings.MaxTokens switch
{
    null => null,           // use model default
    < 1 => null,            // treat invalid as default
    var v => v              // valid value
};

Type guard

static bool IsValidMaxTokens(int? maxTokens) => maxTokens is null or >= 1;

Try / catch

try { var result = await client.GetChatMessageContentsAsync(history, settings); }
catch (ArgumentException ex) when (ex.Message.Contains("MaxTokens"))
{
    logger.LogWarning("Invalid MaxTokens {Val}, retrying with default", settings.MaxTokens);
    settings.MaxTokens = null;
    result = await client.GetChatMessageContentsAsync(history, settings);
}

Prevention

When it happens

Trigger: Setting GeminiPromptExecutionSettings.MaxTokens to 0 or a negative number. The validation runs during request construction (CreateGeminiRequest and similar paths), not at property-set time. Any Google/Gemini connector call path that builds a request invokes this check.

Common situations: Computing MaxTokens dynamically (e.g. maxLength - currentTokens) and hitting a zero or negative result. Defaulting MaxTokens to 0 in a configuration object meaning 'unlimited' when the connector expects null for that. Passing a count from a UI input that was left at 0. Using the same settings object across providers where 0 means 'no limit' in one but is invalid here.

Related errors


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