microsoft/FASTER · error · InvalidDataException

wrong amount of data received from page blob, expected=

Error message

wrong amount of data received from page blob, expected={length}, actual={stream.Position}

What it means

AzureStorageDevice reads a range of bytes from an Azure page blob into a stream and validates that the stream received exactly offset+length bytes. This InvalidDataException is thrown when the copy from the HTTP response completed with fewer (or more) bytes than requested, indicating truncated or corrupted blob data. The library throws it because silently returning short reads would corrupt log replay.

Solutions

  1. Verify the blob exists and is at least offset+length bytes before reading; restore from backup if truncated.
  2. Check that no other process is deleting/truncating blobs (lifecycle policies, competing instances) and that the container is not being emptied.
  3. Retry the read on transient network failures; ensure Azure Storage retry policy is enabled.
  4. Confirm the offset/length passed to the read matches the checkpoint metadata written earlier.

Example fix

// before: reading a range that may exceed blob length
long length = endOffset - offset; // may exceed blob size
await device.ReadAsync(offset, buffer, length);
// after: clamp the read to the actual blob size
var props = await blob.GetPropertiesAsync();
long length = Math.Min(endOffset, props.Value.ContentLength) - offset;
await device.ReadAsync(offset, buffer, length);
Defensive patterns

Strategy: retry

Validate before calling

var props = await blob.GetPropertiesAsync();
if (props.Value.ContentLength < offset + length)
    throw new InvalidOperationException($"blob too short: need {offset + length}, have {props.Value.ContentLength}");

Type guard

static bool IsBlobReadShort(Stream s, long offset, long length) => s.Position != offset + length;

Try / catch

try
{
    await device.ReadAsync(offset, buffer, length);
}
catch (InvalidDataException ex) when (ex.Message.StartsWith("wrong amount of data received from page blob"))
{
    // log, verify blob integrity, optionally retry or restore from checkpoint
}

Prevention

When it happens

Trigger: Calling the page-blob read path (used by Faster's Azure storage device during checkpoint/log reads) when the blob was resized/truncated, the blob was deleted or leased by another process mid-read, the requested [offset, offset+length) range extends past the blob's committed length, or a transient network failure cut the response body short.

Common situations: Reading a FASTER checkpoint whose blob was truncated by a failed upload; a container/blob deleted by lifecycle policy while the device was running; requesting a byte range beyond the blob size after an improper shutdown; intermittent Azure Storage network failures.

Related errors


AI-assisted analysis of microsoft/FASTER@321d872eab (2026-09-15). Data as JSON: /api/errors/0dd75f0335c8baaf. Report an issue: GitHub.

Appendix: source

Thrown at cs/src/devices/AzureStorageDevice/AzureStorageDevice.cs:606

                            }

                            if (length > 0)
                            {
                                var client = (numAttempts > 1 || length == MAX_DOWNLOAD_SIZE) ? blob.Default : blob.Aggressive;

                                var response = await client.DownloadStreamingAsync(
                                    range: new Azure.HttpRange(sourceAddress + offset, length),
                                    conditions: null,
                                    rangeGetContentHash: false,
                                    cancellationToken: this.StorageErrorHandler.Token)
                                    .ConfigureAwait(false);

                                await response.Value.Content.CopyToAsync(stream).ConfigureAwait(false);
                            }

                            if (stream.Position != offset + length)
                            {
                                throw new InvalidDataException($"wrong amount of data received from page blob, expected={length}, actual={stream.Position}");
                            }

                            return length;
                        });

                    readLength -= length;
                    offset += length;
                }
            }
        }

        void TryWriteAsync(BlobEntry blobEntry, IntPtr sourceAddress, ulong destinationAddress, uint numBytesToWrite, long id)
        {
            // If pageBlob is null, it is being created. Attempt to queue the write for the creator to complete after it is done
            if (blobEntry.PageBlob.Default == null
                && blobEntry.TryQueueAction(() => this.WriteToBlobAsync(blobEntry, sourceAddress, destinationAddress, numBytesToWrite, id)))
            {
                return;

View on GitHub (pinned to 321d872eab)