dotnet/orleans · error · ArgumentOutOfRangeException

Value must be positive or Timeout.InfiniteTimeSpan.

Error message

Value must be positive or Timeout.InfiniteTimeSpan.

What it means

Thrown by the MaxPauseBetweenOperationRetries setter on AzureStoragePolicyOptions when the value is <= TimeSpan.Zero and is not Timeout.InfiniteTimeSpan. This bounds the Azure SDK exponential-retry ceiling; a non-positive finite value would make no sense, so it is rejected with an ArgumentOutOfRangeException. Defaults to 1 minute when unset.

Source

Thrown at src/Azure/Shared/Storage/AzureStoragePolicyOptions.cs:56

        public TimeSpan PauseBetweenCreationRetries { get; set; } = TimeSpan.FromSeconds(1);

        /// <summary>
        /// The base delay used by the Azure SDK exponential retry policy.
        /// </summary>
        public TimeSpan PauseBetweenOperationRetries { get; set; } = TimeSpan.FromSeconds(0.8);

        /// <summary>
        /// The maximum delay used by the Azure SDK exponential retry policy.
        /// </summary>
        public TimeSpan MaxPauseBetweenOperationRetries
        {
            get => this.maxPauseBetweenOperationRetries ?? TimeSpan.FromMinutes(1);
            set
            {
                if (value <= TimeSpan.Zero && !value.Equals(Timeout.InfiniteTimeSpan))
                {
                    throw new ArgumentOutOfRangeException(nameof(MaxPauseBetweenOperationRetries), value, "Value must be positive or Timeout.InfiniteTimeSpan.");
                }
                this.maxPauseBetweenOperationRetries = value;
            }
        }

        public TimeSpan CreationTimeout
        {
            get => this.creationTimeout ?? TimeSpan.FromMilliseconds(this.PauseBetweenCreationRetries.TotalMilliseconds * this.MaxCreationRetries * 3);
            set => SetIfValidTimeout(ref this.creationTimeout, value, nameof(CreationTimeout));
        }

        public TimeSpan OperationTimeout
        {
            get => this.operationTimeout ?? TimeSpan.FromSeconds(100);
            set => SetIfValidTimeout(ref this.operationTimeout, value, nameof(OperationTimeout));
        }

        private static void SetIfValidTimeout(ref TimeSpan? field, TimeSpan value, string propertyName)

View on GitHub (pinned to fca799fa70)

Solutions

  1. Set a positive TimeSpan (e.g. TimeSpan.FromMinutes(1)) or Timeout.InfiniteTimeSpan for no cap.
  2. If you want the default, simply do not assign the property (it defaults to 1 minute).
  3. Validate computed delay values before assignment.

Example fix

// before
policy.MaxPauseBetweenOperationRetries = TimeSpan.Zero; // throws
// after
policy.MaxPauseBetweenOperationRetries = TimeSpan.FromMinutes(1); // or leave unset for the default
Defensive patterns

Strategy: validation

Validate before calling

static TimeSpan SafeMaxPause(TimeSpan v) =>
    v > TimeSpan.Zero || v == Timeout.InfiniteTimeSpan ? v : throw new ArgumentOutOfRangeException(nameof(v));

Type guard

static bool IsValidMaxPause(TimeSpan v) => v > TimeSpan.Zero || v == Timeout.InfiniteTimeSpan;

Try / catch

catch (ArgumentOutOfRangeException ex) when (ex.Message.Contains("Value must be positive")) { /* pass a positive TimeSpan or Infinite */ }

Prevention

When it happens

Trigger: Assigning policy.MaxPauseBetweenOperationRetries = TimeSpan.Zero or a negative TimeSpan (e.g. TimeSpan.FromSeconds(-1)) in storage policy configuration.

Common situations: Misreading 0 as 'no cap'; computing a delay that underflows to negative; copying a config snippet that used TimeSpan.Zero intending 'default'.

Understand the failure class

Related errors


AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13). Data as JSON: /api/errors/48d3ba8320fa1386. Report an issue: GitHub.