Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

S3 PutObjectAsync() throws on duplicate trailing header key when SDK retries the request #3154

Closed
dorny opened this issue Jan 22, 2024 · 2 comments · Fixed by #3163
Closed
Assignees
Labels
bug This issue is a bug. p0 This issue is the highest priority s3

Comments

@dorny
Copy link

dorny commented Jan 22, 2024

Describe the bug

S3 PutObjectAsync() throws:

System.ArgumentException: An item with the same key has already been added. Key: x-amz-checksum-crc32c

This consistently happens when:

  • ChecksumAlgorithm is set to ChecksumAlgorithm.CRC32C or any other additional checksum algorithm
  • Checksum is calculated by SDK and send via trailing headers, no pre-calculated value is provided
  • SDK retries the request (e.g. first request fails due to timeout or some other transient error)

Expected Behavior

PutObjectAsync() with checksum algorithm (e.g. CRC32C) can be retried by SDK without throwing exception.

Current Behavior

IAmazonS3.PutObjectAsync() with ChecksumAlgorithm set to CRC32C throws System.ArgumentException when the request is retried by SDK:

Stack trace:

System.ArgumentException: An item with the same key has already been added. Key: x-amz-checksum-crc32c
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
   at Amazon.Runtime.Internal.Util.ChecksumUtils.SetRequestChecksum(IRequest request, String checksumAlgorithm, Boolean fallbackToMD5)
   at Amazon.Runtime.Internal.ChecksumHandler.PreInvoke(IExecutionContext executionContext)
   at Amazon.Runtime.Internal.ChecksumHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.CredentialsRetriever.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.RetryHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.RetryHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.CallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.CallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.S3.Internal.AmazonS3ExceptionHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.ErrorCallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.MetricsHandler.InvokeAsync[T](IExecutionContext executionContext)

Reproduction Steps

using Amazon.Runtime;
using Amazon.S3;
using Amazon.S3.Model;

// Credentials taken from ENV  
var s3 = new AmazonS3Client(new AmazonS3Config
{
    // Unrealistic timeout so SDK will do a retry
    Timeout = TimeSpan.FromMilliseconds(1),
    RetryMode = RequestRetryMode.Standard,
    MaxErrorRetry = 1
});


// Replace the bucket name with some existing writable bucket
const string bucketName = "SOME_BUCKET"; 
const string objectKey = "s3-retry-bug";

// Random data; 
var data = new byte[1024];
Random.Shared.NextBytes(data);

try
{
    await s3.PutObjectAsync(new PutObjectRequest
    {
        BucketName = bucketName,
        Key = objectKey,
        ChecksumAlgorithm = ChecksumAlgorithm.CRC32C,
        InputStream = new MemoryStream(data)
    });
}
catch (ArgumentException)
{
    Console.WriteLine("Bug reproduced");
}

Possible Solution

I think the exception is thrown from here:

request.TrailingHeaders.Add(checksumHeaderKey, string.Empty);

It could probably work if it either checks if the key already exists, or use a setter instead of Add().

Additional Information/Context

No response

AWS .NET SDK and/or Package version used

AWSSDK.S3 3.7.305.7

Targeted .NET Platform

.NET 7

Operating System and version

mcr.microsoft.com/dotnet/aspnet:7.0 running in AWS EKS

@dorny dorny added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Jan 22, 2024
@bhoradc bhoradc self-assigned this Jan 22, 2024
@bhoradc bhoradc added s3 needs-review p0 This issue is the highest priority queued and removed needs-triage This issue or PR still needs to be triaged. needs-review labels Jan 22, 2024
@dscpinheiro dscpinheiro added pending-release This issue will be fixed by an approved PR that hasn't been released yet. and removed queued labels Jan 30, 2024
@dscpinheiro
Copy link
Contributor

Thanks for reporting this problem, we merged a fix earlier today and it's included in the last version of the S3 package (3.7.305.19 as of today).

Copy link

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

@dscpinheiro dscpinheiro removed the pending-release This issue will be fixed by an approved PR that hasn't been released yet. label Jan 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This issue is a bug. p0 This issue is the highest priority s3
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants