Skip to content

@aws-sdk/client-s3 may produce malformed S3 streaming (aws-chunked) data #7509

@mxxk

Description

@mxxk

Checkboxes for prior research

Describe the bug

When S3 receives a streaming body (e.g., STREAMING-AWS4-HMAC-SHA256-PAYLOAD), it requires a minimum chunk size of 8 KiB (see: Signature Calculations for the Authorization Header: Transferring Payload in Multiple Chunks (Chunked Upload) (AWS Signature Version 4)).

Consolidating chunks smaller than 8 KiB is performed by @smithy/util-stream:createBufferedReadable, but createBufferedReadable is only used if requestStreamBufferSize is at least 8 KiB:

updatedBody = getAwsChunkedEncodingStream(
typeof config.requestStreamBufferSize === "number" && config.requestStreamBufferSize >= 8 * 1024
? createBufferedReadable(requestBody, config.requestStreamBufferSize, context.logger)
: requestBody,
{
base64Encoder,
bodyLengthChecker,
checksumLocationName,
checksumAlgorithmFn,
streamHasher,
}
);

The issue is that the default S3Client configuration does not initialize requestStreamBufferSize, so it defaults to 0 and streaming input data is not buffered. This causes multiple problems:

  • If the input stream yields chunks smaller than 8 KiB, S3 rejects the request:

    <?xml version="1.0" encoding="UTF-8"?>
    <Error>
      <Code>InvalidChunkSizeError</Code>
      <Message>Only the last chunk is allowed to have a size less than 8192 bytes</Message>
      <Chunk>2</Chunk>
      <BadChunkSize>1</BadChunkSize>
      <RequestId>MFV9HHNYNX34P0CR</RequestId>
      <HostId>W2Ui7jGmxqjbC4YDNBFlkwMOpvvOLiFfG3/+hKwkDf17y+RjG0DXgK5Ys6E9NqUChL3BAVDdTsM=</HostId>
    </Error>
  • Even worse, if the input stream produces an empty (zero-byte) chunk, the lack of buffering results in the chunk being serialized as 0\r\n\r\n, which prematurely signals the end of the aws-chunked body and causes a malformed trailer error:

    <?xml version="1.0" encoding="UTF-8"?>
    <Error>
      <Code>MalformedTrailerError</Code>
      <Message>The request contained trailing data that was not well-formed or did not conform to our published schema.</Message>
      <RequestId>SQXM6H16K11AK9WQ</RequestId>
      <HostId>esc1L1ccaETEivpB096+T4kI0Q+bM5q7+Q7x6yKoaLe+jfK6FEQmR08EeHljLiqmBBB59a/Sqcmim9F2YQ04VCp7uDOwaf8GpaZyy4+Ondo=</HostId>
    </Error>

The above errors are fixed by explicitly passing the minimum chunk size to S3Client:

 const s3Client = new S3Client({
+  requestStreamBufferSize: 8 * 1024,
 });

Why is it that S3Client does not default requestStreamBufferSize to 8 * 1024, as above? This causes the aforementioned errors, which don't seem obvious (and I personally spent the last few days debugging this).

Regression Issue

  • Select this option if this issue appears to be a regression.

SDK version number

@aws-sdk/[email protected]

Which JavaScript Runtime is this issue in?

Node.js

Details of the browser/Node.js/ReactNative version

Node.js v24.11.0

Reproduction Steps

See https://github.com/mxxk-examples/aws-sdk-js-v3-issue-7509.

Observed Behavior

Depending on the test, S3 errors out with InvalidChunkSizeError or MalformedTrailerError, as seen above.

Expected Behavior

S3Client properly buffers chunks smaller than 8 KiB (which also tolerates empty chunks from the input stream) without the caller needing to know about or supply requestStreamBufferSize.

Possible Solution

Make S3Client default requestStreamBufferSize to 8 * 1024 (8 KiB).

Additional Information/Context

Related issues:

Metadata

Metadata

Labels

closing-soonThis issue will automatically close in 4 days unless further comments are made.feature-requestNew feature or enhancement. May require GitHub community feedback.s3wontfixWe have determined that we will not resolve the issue.workaround-availableThis issue has a work around available.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions