abpframework/abp · critical · AbpException

The data is too large: the maximum chunk count has been exce

Error message

The data is too large: the maximum chunk count has been exceeded!

What it means

The per-BLOB nonce is an 8-byte random base plus a 4-byte chunk index. If the index becomes negative (i.e. it has overflowed the signed 32-bit range), the same nonce would repeat for the same key, which catastrophically breaks AES-GCM confidentiality. CreateChunkNonce refuses to build such a nonce and throws AbpException.

Source

Thrown at framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobEncryptionCodec.cs:477

    internal static void VerifyTerminalRecordCore(IDisposable cipher, byte[] associatedData, byte[] nonce, byte[] tag)
    {
#if NETSTANDARD2_0
        throw new PlatformNotSupportedException("AES-GCM is not available on .NET Standard 2.0!");
#else
        // Throws CryptographicException if the tag is invalid.
        ((AesGcm)cipher).Decrypt(nonce, Array.Empty<byte>(), tag, Array.Empty<byte>(), associatedData);
#endif
    }

    // Nonce = 8-byte random base + 4-byte chunk index; the per-BLOB key (random salt)
    // makes cross-BLOB reuse harmless and the index keeps it unique within the BLOB.
    internal static byte[] CreateChunkNonce(byte[] baseNonce, int chunkIndex)
    {
        if (chunkIndex < 0)
        {
            // A wrapped chunk index would repeat a nonce for the same key, which breaks AES-GCM.
            throw new AbpException("The data is too large: the maximum chunk count has been exceeded!");
        }

        var nonce = new byte[GcmNonceSize];
        Array.Copy(baseNonce, 0, nonce, 0, BaseNonceSize);
        WriteInt32BigEndian(nonce, BaseNonceSize, chunkIndex);
        return nonce;
    }

    internal static byte[] CreateChunkAssociatedData(byte[] associatedDataPrefix, int chunkIndex)
    {
        var associatedData = new byte[associatedDataPrefix.Length + 4];
        Array.Copy(associatedDataPrefix, 0, associatedData, 0, associatedDataPrefix.Length);
        WriteInt32BigEndian(associatedData, associatedDataPrefix.Length, chunkIndex);
        return associatedData;
    }

    // A stream builds one nonce and one associated-data buffer with these, then rewrites only
    // the trailing chunk index per chunk with WriteChunkIndex; both hold the index as their

View on GitHub (pinned to 7ed43b1931)

Solutions

  1. Increase the configured chunk size so the content fits within int.MaxValue chunks.
  2. Split the content into multiple BLOBs each under the chunk-count ceiling.
  3. Cap upload size at the application layer based on chunkSize * int.MaxValue.
  4. Audit chunkSize configuration — it should be in the multi-MB range, not KB.

Example fix

// before
Configure<AbpBlobStoringEncryptionOptions>(o =>
{
    o.ChunkSize = 1024; // 1 KB -> overflows at ~2 TB
});

// after
Configure<AbpBlobStoringEncryptionOptions>(o =>
{
    o.ChunkSize = 4 * 1024 * 1024; // 4 MB -> overflows at ~8 PB
});
Defensive patterns

Strategy: validation

Validate before calling

// Compute the maximum content size for a chunk size and refuse oversized uploads.
static readonly long MaxEncryptedContentBytes =
    (long)AbpBlobStoringEncryptionOptions.DefaultChunkSize * int.MaxValue;

static void AssertSize(long contentLength, int chunkSize)
{
    var max = (long)chunkSize * int.MaxValue;
    if (contentLength > max)
        throw new InvalidOperationException(
            $"Content ({contentLength} B) exceeds the encrypted-BLOB limit ({max} B) for chunkSize={chunkSize}.");
}

Type guard

// Validate chunk size at configuration time.
public sealed record ChunkSize
{
    public int Value { get; }
    public ChunkSize(int bytes)
    {
        if (bytes < 1024 || bytes > 64 * 1024 * 1024)
            throw new ArgumentOutOfRangeException(nameof(bytes), "Use a chunk size between 1 KB and 64 MB.");
        Value = bytes;
    }
}

Try / catch

try
{
    await blob.SaveAsync(name, bigStream);
}
catch (AbpException ex) when (ex.Message.Contains("maximum chunk count has been exceeded"))
{
    // Not retryable as-is: the content is too large for the configured chunk size.
    logger.LogError(ex, "Increase ChunkSize or split the content before retrying.");
    throw;
}

Prevention

When it happens

Trigger: Streaming content whose chunk count exceeds int.MaxValue (~2.1 billion chunks) through the encrypting path, so CreateChunkNonce is called with a wrapped (negative) chunkIndex.

Common situations: A very small configured chunk size combined with extremely large content (e.g. 1 KB chunk size on multi-TB BLOBs); a runaway/miscalibrated producer feeding a tiny chunkSize.

Related errors


AI-assisted analysis of abpframework/abp@7ed43b1931 (2026-08-13). Data as JSON: /api/errors/e1875f9cdde2dcd2. Report an issue: GitHub.