dotnet/efcore · error · InvalidOperationException

The specified 'MaxBatchSize' value

Error message

The specified 'MaxBatchSize' value '{value}' is not valid. It must be a positive number.

What it means

RelationalOptionsExtension.WithMaxBatchSize requires the value be null (unset) or strictly greater than zero; 0 and negatives are rejected. This bounds the number of statements grouped into one multi-statement command during SaveChanges.

Solutions

  1. Pass a positive int (>= 1) for an explicit cap, or leave unset / pass null to use the provider default.
  2. Validate config: treat 0/negative as 'unset' (null).
  3. To disable batching, set MaxBatchSize to 1, not 0.

Example fix

// before
optionsBuilder.UseSqlServer(conn).MaxBatchSize(0);

// after
optionsBuilder.UseSqlServer(conn).MaxBatchSize(1); // or omit to use default
Defensive patterns

Strategy: validation

Validate before calling

static int? SafeMaxBatchSize(string? raw)
    => int.TryParse(raw, out var v) && v > 0 ? v : (int?)null;

Type guard

static bool IsValidMaxBatchSize(int? value) => value is null || value > 0;

Prevention

When it happens

Trigger: Calling optionsBuilder.MaxBatchSize(0) or .MaxBatchSize(-3); computing MaxBatchSize from config that can resolve to 0.

Common situations: Defaulting to 0 to mean 'disabled'; mis-parsed config; tests that pass 0 expecting batching off (you should leave it unset instead).

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/885306632baf1e75. Report an issue: GitHub.

Appendix: source

Thrown at src/EFCore.Relational/Infrastructure/RelationalOptionsExtension.cs:197

    /// <summary>
    ///     The maximum number of statements that will be included in commands sent to the database
    ///     during <see cref="DbContext.SaveChanges()" /> or <see langword="null" /> if none has been set.
    /// </summary>
    public virtual int? MaxBatchSize
        => _maxBatchSize;

    /// <summary>
    ///     Creates a new instance with all options the same as for this instance, but with the given option changed.
    ///     It is unusual to call this method directly. Instead use <see cref="DbContextOptionsBuilder" />.
    /// </summary>
    /// <param name="maxBatchSize">The option to change.</param>
    /// <returns>A new instance with the option changed.</returns>
    public virtual RelationalOptionsExtension WithMaxBatchSize(int? maxBatchSize)
    {
        if (maxBatchSize.HasValue
            && maxBatchSize <= 0)
        {
            throw new InvalidOperationException(RelationalStrings.InvalidMaxBatchSize(maxBatchSize));
        }

        var clone = Clone();

        clone._maxBatchSize = maxBatchSize;

        return clone;
    }

    /// <summary>
    ///     The minimum number of statements that are needed for a multi-statement command sent to the database
    ///     during <see cref="DbContext.SaveChanges()" /> or <see langword="null" /> if none has been set.
    /// </summary>
    public virtual int? MinBatchSize
        => _minBatchSize;

    /// <summary>
    ///     Creates a new instance with all options the same as for this instance, but with the given option changed.

View on GitHub (pinned to 3a2006ef56)