dotnet/orleans · error · ArgumentNullException
Value cannot be null. (Parameter 'clusterId')
Error message
Value cannot be null. (Parameter 'clusterId')
What it means
Thrown by OrleansSiloInstanceManager.DeleteTableEntries(string clusterId) when clusterId is null. The cluster/deployment id is the Azure Table partition key for membership entries; a null cannot scope the deletion, so the call is rejected with ArgumentNullException(nameof(clusterId)). This is a defensive guard before ReadAllTableEntriesForPartitionAsync.
Source
Thrown at src/Azure/Orleans.Clustering.AzureStorage/OrleansSiloInstanceManager.cs:208
sb.AppendLine(string.Format("[IP {0}:{1}:{2}, {3}, Instance={4}, Status={5}]", entry.Address, entry.Port, entry.Generation,
entry.HostName, entry.SiloName, entry.Status));
}
return sb.ToString();
}
internal Task<string> MergeTableEntryAsync(SiloInstanceTableEntry data)
{
return storage.MergeTableEntryAsync(data, AzureTableUtils.ANY_ETAG); // we merge this without checking eTags.
}
internal Task<(SiloInstanceTableEntry? Entity, string? ETag)> ReadSingleTableEntryAsync(string partitionKey, string rowKey)
{
return storage.ReadSingleTableEntryAsync(partitionKey, rowKey);
}
internal async Task<int> DeleteTableEntries(string clusterId)
{
if (clusterId == null) throw new ArgumentNullException(nameof(clusterId));
var entries = await storage.ReadAllTableEntriesForPartitionAsync(clusterId);
await DeleteEntriesBatch(entries);
return entries.Count;
}
public async Task CleanupDefunctSiloEntries(DateTimeOffset beforeDate)
{
var entriesList = (await FindAllSiloEntries())
.Where(entry => !SiloInstanceTableEntry.IsVersionRow(entry.Entity.RowKey)
&& entry.Item1.Status != INSTANCE_STATUS_ACTIVE
&& entry.Item1.Timestamp < beforeDate)
.ToList();
// Defunct-row cleanup intentionally does not advance the membership snapshot fence.
await DeleteEntriesBatch(entriesList);View on GitHub (pinned to fca799fa70)
Solutions
- Pass the concrete cluster id (the DeploymentId used when the cluster was created).
- Bind clusterId from configuration and validate non-null before teardown.
- Use `clusterId ?? throw new InvalidOperationException("ClusterId not configured.")` for a clearer upstream error.
- Add a startup check that the deployment-id option is set on all silos.
Example fix
// before
await manager.DeleteTableEntries(clusterId: null);
// after
var cid = options.ClusterId ?? throw new InvalidOperationException("ClusterId missing.");
await manager.DeleteTableEntries(cid); Defensive patterns
Strategy: validation
Validate before calling
if (string.IsNullOrWhiteSpace(clusterId))
throw new InvalidOperationException("ClusterId/DeploymentId is not configured."); Type guard
static bool HasClusterId(string? s) => !string.IsNullOrWhiteSpace(s);
Try / catch
try { await manager.DeleteTableEntries(clusterId); }
catch (ArgumentNullException ex) when (ex.ParamName == nameof(clusterId)) { /* bind option */ } Prevention
- Bind and validate ClusterId/DeploymentId at startup.
- Never invoke teardown tools with unbound options.
- Coalesce null with a clear upstream error.
When it happens
Trigger: Calling DeleteTableEntries(null), e.g. an admin/cleanup tool that read the cluster id from a missing config value, or an automated teardown script with an unbound option.
Common situations: Cluster teardown tooling with an unbound ClusterId/DeploymentId option; misconfigured test harness; refactor that renamed the option but left the binding stale.
Related errors
- The table version entry must have a membership version.
- The table version row does not contain a membership version.
- The membership table does not contain a version row.
- SuspectingSilos.Length of {0} as read from Azure table is no
- Could not find table version row or found too many entries.
AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13).
Data as JSON: /api/errors/8ead0a384ed5a332.
Report an issue: GitHub.