dotnet/orleans · error · ArgumentOutOfRangeException

MaxMetadataOnlyConflictRetries must be non-negative.

Error message

MaxMetadataOnlyConflictRetries must be non-negative.

What it means

Thrown during AzureBlobJournalStorageShared construction when AzureBlobJournalStorageOptions.MaxMetadataOnlyConflictRetries is negative. This option caps how many times the storage layer retries in place after a metadata-only ETag conflict before falling back to full recovery via InconsistentStateException. A negative value is meaningless and is rejected at construction time.

Source

Thrown at src/Azure/Orleans.Journaling.AzureStorage/AzureBlobJournalStorage.cs:1137

    {
        public AzureBlobJournalStorageShared(
            ILogger<AzureBlobJournalStorage> logger,
            IOptions<AzureBlobJournalStorageOptions> options,
            BlobClientProvider blobClientProvider,
            AzureBlobJournalStorageInstruments instruments,
            string? mimeType = null,
            string? journalFormatKey = null)
        {
            ArgumentNullException.ThrowIfNull(logger);
            ArgumentNullException.ThrowIfNull(options);
            ArgumentNullException.ThrowIfNull(blobClientProvider);

            Logger = logger;
            Options = options.Value;
            ArgumentNullException.ThrowIfNull(Options);
            if (Options.MaxMetadataOnlyConflictRetries < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(options), $"{nameof(AzureBlobJournalStorageOptions.MaxMetadataOnlyConflictRetries)} must be non-negative.");
            }

            if (Options.MetadataOnlyConflictInitialBackoff < TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException(nameof(options), $"{nameof(AzureBlobJournalStorageOptions.MetadataOnlyConflictInitialBackoff)} must be non-negative.");
            }

            if (Options.MetadataOnlyConflictMaxBackoff < TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException(nameof(options), $"{nameof(AzureBlobJournalStorageOptions.MetadataOnlyConflictMaxBackoff)} must be non-negative.");
            }

            MimeType = mimeType;
            JournalFormatKey = journalFormatKey;
            BlobClientProvider = blobClientProvider;
            Instruments = instruments;
        }

View on GitHub (pinned to fca799fa70)

Solutions

  1. Set MaxMetadataOnlyConflictRetries to 0 (disable in-place retry) or a positive integer; keep the default of 5 unless you have measured conflict rates.
  2. If you intended 'retry forever', raise the cap rather than using a negative value; the design requires a bounded retry count.
  3. Validate the option in your configuration validator (IValidateOptions<AzureBlobJournalStorageOptions>) so misconfigurations surface at startup, not at first use.

Example fix

// before
options.MaxMetadataOnlyConflictRetries = -1;

// after
options.MaxMetadataOnlyConflictRetries = 0; // disable in-place retry; always go through recovery
Defensive patterns

Strategy: validation

Validate before calling

if (options.MaxMetadataOnlyConflictRetries < 0)
    throw new ArgumentOutOfRangeException(nameof(options.MaxMetadataOnlyConflictRetries), "Must be non-negative.");
// or register IValidateOptions<AzureBlobJournalStorageOptions>:
// services.AddOptions<AzureBlobJournalStorageOptions>().ValidateDataAnnotations();

Prevention

When it happens

Trigger: Constructing AzureBlobJournalStorageShared (directly or via the DI provider) after setting options.MaxMetadataOnlyConflictRetries to a value less than 0. Validation runs in the shared-state constructor before any storage operation.

Common situations: Setting the option to -1 intending 'retry forever' (not supported); misreading the default of 5 and setting it to a sentinel negative value; loading options from configuration with a typo that yields a negative integer.

Related errors


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