{"record":{"id":"e1875f9cdde2dcd2","repo":"abpframework/abp","slug":"the-data-is-too-large-the-maximum-chunk-count-has","errorCode":null,"errorMessage":"The data is too large: the maximum chunk count has been exceeded!","messagePattern":"The data is too large: the maximum chunk count has been exceeded!","errorType":"exception","errorClass":"AbpException","httpStatus":null,"severity":"critical","filePath":"framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobEncryptionCodec.cs","lineNumber":477,"sourceCode":"\n    internal static void VerifyTerminalRecordCore(IDisposable cipher, byte[] associatedData, byte[] nonce, byte[] tag)\n    {\n#if NETSTANDARD2_0\n        throw new PlatformNotSupportedException(\"AES-GCM is not available on .NET Standard 2.0!\");\n#else\n        // Throws CryptographicException if the tag is invalid.\n        ((AesGcm)cipher).Decrypt(nonce, Array.Empty<byte>(), tag, Array.Empty<byte>(), associatedData);\n#endif\n    }\n\n    // Nonce = 8-byte random base + 4-byte chunk index; the per-BLOB key (random salt)\n    // makes cross-BLOB reuse harmless and the index keeps it unique within the BLOB.\n    internal static byte[] CreateChunkNonce(byte[] baseNonce, int chunkIndex)\n    {\n        if (chunkIndex < 0)\n        {\n            // A wrapped chunk index would repeat a nonce for the same key, which breaks AES-GCM.\n            throw new AbpException(\"The data is too large: the maximum chunk count has been exceeded!\");\n        }\n\n        var nonce = new byte[GcmNonceSize];\n        Array.Copy(baseNonce, 0, nonce, 0, BaseNonceSize);\n        WriteInt32BigEndian(nonce, BaseNonceSize, chunkIndex);\n        return nonce;\n    }\n\n    internal static byte[] CreateChunkAssociatedData(byte[] associatedDataPrefix, int chunkIndex)\n    {\n        var associatedData = new byte[associatedDataPrefix.Length + 4];\n        Array.Copy(associatedDataPrefix, 0, associatedData, 0, associatedDataPrefix.Length);\n        WriteInt32BigEndian(associatedData, associatedDataPrefix.Length, chunkIndex);\n        return associatedData;\n    }\n\n    // A stream builds one nonce and one associated-data buffer with these, then rewrites only\n    // the trailing chunk index per chunk with WriteChunkIndex; both hold the index as their","sourceCodeStart":459,"sourceCodeEnd":495,"githubUrl":"https://github.com/abpframework/abp/blob/7ed43b1931b9df46a50c0c59148a18645641d0df/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobEncryptionCodec.cs#L459-L495","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Increase the configured chunk size so the content fits within int.MaxValue chunks.","Split the content into multiple BLOBs each under the chunk-count ceiling.","Cap upload size at the application layer based on chunkSize * int.MaxValue.","Audit chunkSize configuration — it should be in the multi-MB range, not KB."],"exampleFix":"// before\nConfigure<AbpBlobStoringEncryptionOptions>(o =>\n{\n    o.ChunkSize = 1024; // 1 KB -> overflows at ~2 TB\n});\n\n// after\nConfigure<AbpBlobStoringEncryptionOptions>(o =>\n{\n    o.ChunkSize = 4 * 1024 * 1024; // 4 MB -> overflows at ~8 PB\n});","handlingStrategy":"validation","validationCode":"// Compute the maximum content size for a chunk size and refuse oversized uploads.\nstatic readonly long MaxEncryptedContentBytes =\n    (long)AbpBlobStoringEncryptionOptions.DefaultChunkSize * int.MaxValue;\n\nstatic void AssertSize(long contentLength, int chunkSize)\n{\n    var max = (long)chunkSize * int.MaxValue;\n    if (contentLength > max)\n        throw new InvalidOperationException(\n            $\"Content ({contentLength} B) exceeds the encrypted-BLOB limit ({max} B) for chunkSize={chunkSize}.\");\n}","typeGuard":"// Validate chunk size at configuration time.\npublic sealed record ChunkSize\n{\n    public int Value { get; }\n    public ChunkSize(int bytes)\n    {\n        if (bytes < 1024 || bytes > 64 * 1024 * 1024)\n            throw new ArgumentOutOfRangeException(nameof(bytes), \"Use a chunk size between 1 KB and 64 MB.\");\n        Value = bytes;\n    }\n}","tryCatchPattern":"try\n{\n    await blob.SaveAsync(name, bigStream);\n}\ncatch (AbpException ex) when (ex.Message.Contains(\"maximum chunk count has been exceeded\"))\n{\n    // Not retryable as-is: the content is too large for the configured chunk size.\n    logger.LogError(ex, \"Increase ChunkSize or split the content before retrying.\");\n    throw;\n}","preventionTips":["Set ChunkSize to multi-MB (e.g. 4–8 MB) so the int32 chunk ceiling is many PB, not TB.","Enforce an upload-size cap derived from chunkSize * int.MaxValue at the API boundary.","Audit chunkSize in configuration reviews.","Split very large content into multiple BLOBs."],"tags":["crypto","nonce","capacity","aes-gcm"],"backgroundTag":null,"analyzedSha":"7ed43b1931b9df46a50c0c59148a18645641d0df","analyzedAt":"2026-08-13T16:26:11.351Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}