{"record":{"id":"6d919278e667d936","repo":"abpframework/abp","slug":"the-encrypted-blob-is-corrupted-or-has-an-invalid-6d9192","errorCode":null,"errorMessage":"The encrypted BLOB is corrupted or has an invalid format: missing terminal record!","messagePattern":"The encrypted BLOB is corrupted or has an invalid format: missing terminal record!","errorType":"exception","errorClass":"AbpException","httpStatus":null,"severity":"error","filePath":"framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobEncryptionCodec.cs","lineNumber":526,"sourceCode":"        return associatedData;\n    }\n\n    internal static void WriteChunkIndex(byte[] nonceOrAssociatedData, 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        WriteInt32BigEndian(nonceOrAssociatedData, nonceOrAssociatedData.Length - 4, chunkIndex);\n    }\n\n    internal static int GetCipherChunkSize(byte[] lengthPrefix, int maxCipherChunkSize)\n    {\n        if (lengthPrefix.Length == 0)\n        {\n            throw new AbpException(\"The encrypted BLOB is corrupted or has an invalid format: missing terminal record!\");\n        }\n\n        if (lengthPrefix.Length < ChunkLengthPrefixSize)\n        {\n            throw new AbpException(\"The encrypted BLOB is corrupted or has an invalid format: truncated chunk!\");\n        }\n\n        var cipherChunkSize = ReadInt32BigEndian(lengthPrefix, 0);\n        if (cipherChunkSize < 0 || cipherChunkSize > maxCipherChunkSize)\n        {\n            throw new AbpException(\"The encrypted BLOB is corrupted or has an invalid format: invalid chunk length!\");\n        }\n\n        return cipherChunkSize;\n    }\n\n    internal static byte[]? ReadExactly(Stream stream, int count)\n    {","sourceCodeStart":508,"sourceCodeEnd":544,"githubUrl":"https://github.com/abpframework/abp/blob/7ed43b1931b9df46a50c0c59148a18645641d0df/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobEncryptionCodec.cs#L508-L544","documentation":"GetCipherChunkSize reads the 4-byte length prefix of the next record. A zero-length prefix (EOF) where a terminal record was expected means the BLOB ended without its authenticated terminal record, so completeness cannot be verified. The codec throws rather than treat a truncated stream as complete.","triggerScenarios":"Decrypting a BLOB whose cipher stream returns zero bytes for the length-prefix read because the data was truncated before the terminal record, or the underlying stream is empty/incomplete.","commonSituations":"A truncated upload (network drop, partial write, interrupted multipart upload); corrupted/empty object in storage; wrong format version; storage provider returned a partial object.","solutions":["Re-upload the BLOB and ensure the upload completes fully.","Verify the stored object's byte length against the expected ciphertext length.","Check the storage backend for partial/interrupted writes and clean them up.","Confirm the BLOB was written by a compatible version of the encryption codec."],"exampleFix":"// before — swallowing storage errors and proceeding\ntry { await blob.SaveAsync(name, data); } catch { /* ignored */ }\nvar read = await blob.GetAllBytesAsync(name); // throws [88]\n\n// after — ensure the write actually completes\nawait blob.SaveAsync(name, data);\nvar stat = await provider.GetOrNullAsync(name);\nif (stat == null) throw new InvalidOperationException(\"upload lost\");","handlingStrategy":"try-catch","validationCode":"// Verify object presence and minimum length before decrypting.\nvar info = await provider.GetOrNullAsync(name);\nif (info == null)\n    throw new FileNotFoundException($\"BLOB '{name}' not found.\");\nif (info.ContentLength < Magic.Length /* magic header */)\n    throw new InvalidOperationException($\"BLOB '{name}' is too short to be a valid encrypted container.\");","typeGuard":"public sealed record VerifiedEncryptedBlob(string Name, long ContentLength)\n{\n    public static VerifiedEncryptedBlob Check(string name, long len)\n    {\n        if (len < 16) throw new InvalidOperationException(\"blob too short to be a valid encrypted container\");\n        return new VerifiedEncryptedBlob(name, len);\n    }\n}","tryCatchPattern":"try\n{\n    return await blob.GetAllBytesAsync(name);\n}\ncatch (AbpException ex) when (ex.Message.Contains(\"missing terminal record\"))\n{\n    logger.LogError(ex, \"BLOB '{Name}' is truncated; re-upload from a known-good source.\", name);\n    // Surface to caller; do not retry the same corrupted object.\n    throw;\n}","preventionTips":["Ensure uploads complete fully (verify status/ETag) before reads.","Store and compare a checksum alongside each BLOB.","Clean up partial/interrupted multipart uploads in storage.","Confirm writer and reader use the same codec version."],"tags":["crypto","integrity","corruption","truncation"],"backgroundTag":null,"analyzedSha":"7ed43b1931b9df46a50c0c59148a18645641d0df","analyzedAt":"2026-08-13T16:26:11.351Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}