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
- Chunk the input into batches of at most MaxBulkUpdateRows before calling, using entries.BatchIEnumerable(100) (the helper the rest of the codebase already uses).
- If you genuinely need larger logical batches, keep MaxBulkUpdateRows <= 100 (Azure's hard limit) and loop over chunks rather than raising the cap.
- 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
- Always chunk with entries.BatchIEnumerable(max)
- Never set MaxBulkUpdateRows > 100 (Azure entity-group transaction limit)
- Log the actual count alongside the limit when rejecting
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
- The collection must contain between 1 and {maxTransactionSiz
- Table name "{tableName}" is invalid according to the followi
- Unknown Playground:Storage:Provider value '{storageProvider}
- The table version entry must have a membership version.
- Value cannot be null. (Parameter 'clusterId')
AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13).
Data as JSON: /api/errors/019c2599ee4e3e36.
Report an issue: GitHub.