dotnet/orleans · critical · InvalidOperationException

The membership table does not contain a version row.

Error message

The membership table does not contain a version row.

What it means

Thrown by AzureBasedMembershipTable.Convert when, after iterating all entries, no table version row was found (tableVersion stayed null). Orleans requires exactly one version row per partition to coordinate membership updates; its absence means the table is uninitialized or the version row was deleted. InvalidOperationException is thrown and the outer catch logs via LogErrorParsingMembershipTableData.

Source

Thrown at src/Azure/Orleans.Clustering.AzureStorage/AzureBasedMembershipTable.cs:202

                        continue;
                    }
                    else
                    {
                        try
                        {

                            MembershipEntry membershipEntry = Parse(tableEntry);
                            memEntries.Add(new Tuple<MembershipEntry, string>(membershipEntry, tuple.ETag));
                        }
                        catch (Exception exc)
                        {
                            LogErrorParsingMembershipTableDataIgnoring(exc, tableEntry);
                        }
                    }
                }
                var data = new MembershipTableData(
                    memEntries,
                    tableVersion ?? throw new InvalidOperationException("The membership table does not contain a version row."));
                return data;
            }
            catch (Exception exc)
            {
                LogErrorParsingMembershipTableData(exc, new(entries));
                throw;
            }
        }

        private static MembershipEntry Parse(SiloInstanceTableEntry tableEntry)
        {
            var parse = new MembershipEntry
            {
                HostName = tableEntry.HostName!,
                Status = (SiloStatus)Enum.Parse(typeof(SiloStatus), tableEntry.Status!)
            };

            if (!string.IsNullOrEmpty(tableEntry.ProxyPort))

View on GitHub (pinned to fca799fa70)

Solutions

  1. Recreate the version row: insert an entity with PartitionKey=<deploymentId>, RowKey='Version', MembershipVersion='0' (or the current max), then restart silos.
  2. If acceptable, delete the whole partition and let Orleans reinitialize the membership table.
  3. Upgrade all silos to a consistent Orleans version so version-row creation logic agrees.
  4. Audit cleanup/management code for any path that could delete the version row.

Example fix

// via Azure Data Table SDK
var versionRow = new TableEntity(deploymentId, "Version") {
    ["MembershipVersion"] = "0"
};
await tableClient.UpsertEntityAsync(versionRow);
Defensive patterns

Strategy: try-catch

Validate before calling

// after reading entries, verify a version row exists before consuming:
bool hasVersion = entries.Any(e => e.Entity.RowKey == "Version");
if (!hasVersion) throw new InvalidOperationException("Recreate the Version row.");

Try / catch

try { var data = await membershipTable.ReadMembershipTableAsync(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("does not contain a version row"))
{ /* recreate Version row with MembershipVersion=0 and retry */ }

Prevention

When it happens

Trigger: Reading membership when the partition contains silo rows but no TABLE_VERSION_ROW. Happens if the version row was manually removed, a cleanup deleted it, or table initialization failed partway.

Common situations: Manual deletion of the version row in Azure Storage Explorer; a buggy cleanup agent that removed boundary/version rows; partial init where silos registered but the version row insert failed; schema drift between Orleans versions.

Related errors


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