dotnet/orleans · error · ArgumentNullException

Value cannot be null. (Parameter 'collection')

Error message

Value cannot be null. (Parameter 'collection')

What it means

Thrown by AzureTableDataManager.DeleteTableEntriesAsync when the collection argument is null. The method performs a batch (transaction) delete of pre-existing entities by eTag, so a null list is rejected before any batch is built.

Source

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

        public Task<List<(T Entity, string ETag)>> ReadAllTableEntriesAsync(
            CancellationToken cancellationToken = default)
        {
            return ReadTableEntriesAndEtagsAsync(null, cancellationToken);
        }

        /// <summary>
        /// Deletes a set of already existing data entries in the table, by using eTag.
        /// Fails if the data does not already exist or if eTag does not match.
        /// </summary>
        /// <param name="collection">Data entries and their corresponding etags to be deleted from the table.</param>
        /// <returns>Completion promise for this storage operation.</returns>
        public async Task DeleteTableEntriesAsync(List<(T Entity, string ETag)> collection)
        {
            const string operation = "DeleteTableEntries";
            var startTime = DateTime.UtcNow;
            LogTraceTableEntries(Logger, operation, new(collection), TableName);

            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 delete - max " + this.StoragePolicyOptions.MaxBulkUpdateRows);
            }

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

            try
            {
                var entityBatch = new List<TableTransactionAction>();
                foreach (var tuple in collection)
                {
                    T item = tuple.Entity;

View on GitHub (pinned to fca799fa70)

Solutions

  1. Pass a non-null List<(T Entity, string ETag)> (use an empty list when there is nothing to delete — the method returns early for count 0).
  2. Coalesce nulls to empty lists at the call site.
  3. Use '?? new()' or '?? Enumerable.Empty' patterns when sourcing the list.

Example fix

// before
await manager.DeleteTableEntriesAsync(maybeEntries); // maybeEntries null
// after
await manager.DeleteTableEntriesAsync(maybeEntries ?? new List<(MyEntity Entity, string ETag)>());
Defensive patterns

Strategy: validation

Validate before calling

var entries = collection ?? new List<(T Entity, string ETag)>();
await manager.DeleteTableEntriesAsync(entries);

Type guard

static bool HasCollection<T>(List<(T Entity, string ETag)>? c) => c is not null;

Try / catch

catch (ArgumentNullException ex) when (ex.ParamName == "collection") { /* coalesce null to an empty list */ }

Prevention

When it happens

Trigger: Calling DeleteTableEntriesAsync(null) — e.g. a cleanup routine that returned null instead of an empty list, or a deserialization that produced null.

Common situations: Code that builds a list conditionally and forgets the empty-default; aggregating deletables where no candidates exist; refactor that changed the list source.

Related errors


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