dotnet/orleans · error · InvalidOperationException

Azure Table journal row "{entity.RowKey}" contains a non-con

Error message

Azure Table journal row "{entity.RowKey}" contains a non-contiguous binary property "{propertyName}".

What it means

A journal data row in Azure Table storage was read with a gap in its chunk sequence: one of the Data00..Data14 properties was missing while a later-numbered property was present. ReadChunks (AzureTableJournalStorage.cs:892) requires chunks to be contiguous from Data00 onward, so a hole means the row is structurally invalid and cannot be safely replayed. The writer (AzureTableJournalStorage.cs:868) never produces gaps, so this points to external modification or partial/corrupted writes.

Source

Thrown at src/Azure/Orleans.Journaling.AzureStorage/AzureTableJournalStorage.cs:911

        TableEntity entity,
        ArcBufferWriter buffer,
        IJournalStorageConsumer consumer,
        IJournalMetadata metadata)
    {
        var length = 0L;
        var foundChunk = false;
        var foundGap = false;
        foreach (var propertyName in ChunkPropertyNames)
        {
            if (!entity.TryGetValue(propertyName, out var value))
            {
                foundGap = true;
                continue;
            }

            if (foundGap)
            {
                throw new InvalidOperationException(
                    $"Azure Table journal row \"{entity.RowKey}\" contains a non-contiguous binary property \"{propertyName}\".");
            }

            ReadOnlyMemory<byte> chunk = value switch
            {
                byte[] bytes => bytes,
                BinaryData binaryData => binaryData.ToMemory(),
                _ => throw new InvalidOperationException(
                    $"Azure Table journal row \"{entity.RowKey}\" property \"{propertyName}\" is not a binary value."),
            };

            if (chunk.IsEmpty)
            {
                throw new InvalidOperationException(
                    $"Azure Table journal row \"{entity.RowKey}\" property \"{propertyName}\" is empty.");
            }

            foundChunk = true;

View on GitHub (pinned to fca799fa70)

Solutions

  1. Identify and remove/replace the corrupted row(s) in the journal partition (the RowKey in the message tells you which one), then let the journaling layer rebuild via a Replace.
  2. Ensure no external process or alternate code path writes DataNN properties into the journal table — only AzureTableJournalStorage should mutate these rows.
  3. If migrating from a legacy schema, run a clean Replace to publish a fresh generation rather than partially patching existing rows.
  4. Inspect the partition in Storage Explorer for rows where the set of DataNN keys is non-contiguous.
Defensive patterns

Strategy: try-catch

Try / catch

try { await journalStorage.ReadAsync(...); }
catch (InvalidOperationException ex) when (ex.Message.Contains("non-contiguous binary property"))
{
    // Log RowKey, quarantine the partition, and trigger a journal Replace to rebuild.
}

Prevention

When it happens

Trigger: Returned by ReadChunks when, while iterating ChunkPropertyNames in order, entity.TryGetValue returns false for some DataNN (setting foundGap) and then returns true for a later DataMM. Triggered on any Read/ReadAsync against a row whose data was truncated or hand-edited in the table.

Common situations: An operator or migration script deleted a single DataNN property from a row; a partial transaction left some chunk properties unwritten; a different/older writer version that used a different chunk layout co-exists in the same table; tools like Storage Explorer were used to edit entities.

Related errors


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