dotnet/efcore · error · InvalidOperationException

The specified 'CommandTimeout' value

Error message

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

What it means

RelationalOptionsExtension.WithCommandTimeout guards its input: a negative value is rejected (commandTimeout is < 0). null means 'unset' and is allowed, and 0 is permitted by the guard (the message wording 'positive number' is looser than the code). The exception is thrown eagerly when the option is set, not at query time.

Solutions

  1. Pass null to clear the timeout, or a non-negative int (the guard accepts 0).
  2. Validate user-supplied input (env var, config) before passing it: coerce negatives to null or a sensible default.
  3. If you meant 'unlimited'/default, pass null rather than a sentinel like -1.

Example fix

// before
var timeout = int.Parse(config["CommandTimeout"]!); // could be -1
optionsBuilder.UseSqlServer(conn).CommandTimeout(timeout);

// after
var raw = config["CommandTimeout"];
var timeout = int.TryParse(raw, out var t) && t >= 0 ? t : (int?)null;
optionsBuilder.UseSqlServer(conn).CommandTimeout(timeout);
Defensive patterns

Strategy: validation

Validate before calling

// Coerce user-supplied timeout to null-or-non-negative before setting the option.
static int? SafeCommandTimeout(string? raw)
    => int.TryParse(raw, out var v) && v >= 0 ? v : (int?)null;

Type guard

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

Prevention

When it happens

Trigger: Calling optionsBuilder.UseXxx(...).CommandTimeout(-5), or RelationalOptionsExtension.WithCommandTimeout(-1) directly; computing the timeout from configuration and feeding a negative parsed value.

Common situations: Parsing CommandTimeout from an environment variable / appsettings that can be negative or unset (parsed as -1); sign errors in arithmetic; unit tests injecting bad values.

Understand the failure class

Related errors


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

Appendix: source

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

    }

    /// <summary>
    ///     The command timeout, or <see langword="null" /> if none has been set.
    /// </summary>
    public virtual int? CommandTimeout
        => _commandTimeout;

    /// <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="commandTimeout">The option to change.</param>
    /// <returns>A new instance with the option changed.</returns>
    public virtual RelationalOptionsExtension WithCommandTimeout(int? commandTimeout)
    {
        if (commandTimeout is < 0)
        {
            throw new InvalidOperationException(RelationalStrings.InvalidCommandTimeout(commandTimeout));
        }

        var clone = Clone();

        clone._commandTimeout = commandTimeout;

        return clone;
    }

    /// <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.

View on GitHub (pinned to 3a2006ef56)