dotnet/orleans · error · ArgumentOutOfRangeException

Too many rows for bulk update - max {this.StoragePolicyOptio

Error message

Too many rows for bulk update - max {this.StoragePolicyOptions.MaxBulkUpdateRows}

What it means

ArgumentOutOfRangeException thrown when the collection passed to BulkInsertTableEntries has more rows than AzureStoragePolicyOptions.MaxBulkUpdateRows (default 100). The limit reflects the Azure Table Storage entity-group transaction cap of 100 operations per batch, so the library refuses an oversized single transaction up front.

Source

Thrown at src/Azure/Shared/Storage/AzureTableDataManager.cs:572

            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }

        /// <summary>
        /// Inserts a set of new data entries into the table.
        /// Fails if the data does already exists.
        /// </summary>
        /// <param name="collection">Data entries to be inserted into the table.</param>
        /// <returns>Completion promise for this storage operation.</returns>
        public async Task BulkInsertTableEntries(IReadOnlyCollection<T> collection)
        {
            const string operation = "BulkInsertTableEntries";
            if (collection == null) throw new ArgumentNullException(nameof(collection));
            if (collection.Count > this.StoragePolicyOptions.MaxBulkUpdateRows)
            {
                throw new ArgumentOutOfRangeException(nameof(collection), collection.Count,
                        "Too many rows for bulk update - max " + this.StoragePolicyOptions.MaxBulkUpdateRows);
            }

            if (collection.Count == 0)
            {
                return;
            }

            var startTime = DateTime.UtcNow;
            LogTraceTableEntriesCount(Logger, operation, collection.Count, TableName);
            try
            {
                var entityBatch = new List<TableTransactionAction>(collection.Count);
                foreach (T entry in collection)
                {
                    entityBatch.Add(new TableTransactionAction(TableTransactionActionType.Add, entry));
                }

View on GitHub (pinned to fca799fa70)

Solutions

  1. Chunk the input into batches of at most MaxBulkUpdateRows before calling, using entries.BatchIEnumerable(100) (the helper the rest of the codebase already uses).
  2. If you genuinely need larger logical batches, keep MaxBulkUpdateRows <= 100 (Azure's hard limit) and loop over chunks rather than raising the cap.
  3. Confirm the configured AzureStoragePolicyOptions.MaxBulkUpdateRows value matches what you chunk against.

Example fix

// before
await mgr.BulkInsertTableEntries(allEntries); // throws if > MaxBulkUpdateRows

// after
foreach (var batch in allEntries.BatchIEnumerable(mgr.StoragePolicyOptions.MaxBulkUpdateRows))
{
    await mgr.BulkInsertTableEntries(batch);
}
Defensive patterns

Strategy: validation

Validate before calling

var max = mgr.StoragePolicyOptions.MaxBulkUpdateRows;
if (entries.Count > max)
    throw new ArgumentOutOfRangeException(nameof(entries), entries.Count, $"max {max}");
await mgr.BulkInsertTableEntries(entries);

Type guard

static bool WithinBulkLimit(IReadOnlyCollection<T> c, int max) => c.Count <= max && c.Count > 0;

Prevention

When it happens

Trigger: Passing a collection whose Count exceeds MaxBulkUpdateRows. The check is on IReadOnlyCollection.Count, so any materialized list/enumerable over the configured limit triggers it before any storage call.

Common situations: Bulk-importing reminders/membership/grain-directory rows in one shot, or a producer that accumulates more than 100 entries between flushes. Developers who raised MaxBulkUpdateRows beyond 100 expecting Azure to accept it, or who forgot to chunk their input.

Related errors


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