How Deleting package-lock.json Broke AWS S3 Upload Function

How Deleting package-lock.json
Broke AWS S3 Upload Function
Problem Introduction
In a web project using @aws-sdk/client-s3
(AWS SDK v3), I recently encountered an unexpected error while uploading files from the browser to S3:
The strange part?
This code had worked perfectly fine before. Nothing in the file upload logic had changed. Yet suddenly, the upload crashed at runtime.
Root Cause of the Error
First, I opened the Console tab in the browser’s developer tools and saw the following error
TypeError: readableStream.getReader is not a function
at getAwsChunkedEncodingStream (http://localhost:4300/admin/vendor.7c254d69975f6ea5.js:106506:35)
at http://localhost:4300/admin/vendor.7c254d69975f6ea5.js:99876:27
But wait, wait, wait! The error came directly from a library? What’s going on here? I needed to dig deeper, so I switched to the Sources tab to check the code in detail.
Here is the code in detail. As you can see, this line const reader = readableStream.getReader();
tries to get a reader from readableStream
.`
export const getAwsChunkedEncodingStream = (readableStream, options) => {
const { base64Encoder, bodyLengthChecker, checksumAlgorithmFn, checksumLocationName, streamHasher } = options;
const checksumRequired = base64Encoder !== undefined &&
bodyLengthChecker !== undefined &&
checksumAlgorithmFn !== undefined &&
checksumLocationName !== undefined &&
streamHasher !== undefined;
const digest = checksumRequired ? streamHasher(checksumAlgorithmFn, readableStream) : undefined;
const reader = readableStream.getReader(); # getReader is undefined
return new ReadableStream({
async pull(controller) {
const { value, done } = await reader.read();
if (done) {
controller.enqueue(`0\r\n`);
if (checksumRequired) {
const checksum = base64Encoder(await digest);
controller.enqueue(`${checksumLocationName}:${checksum}\r\n`);
controller.enqueue(`\r\n`);
}
controller.close();
}
else {
controller.enqueue(`${(bodyLengthChecker(value) || 0).toString(16)}\r\n${value}\r\n`);
}
},
});
};
I wondered: which line of code was calling that function? It turned out that the following code was responsible. After adding some console logs to check, I realized that the uploadCommand was triggered by the client.
const client = this.getS3Client(credentials);
const uploadCommand = new PutObjectCommand({
Bucket: this.bucket,
Key: fileKey,
Body: file,
});
return client.send(uploadCommand).then(() => fileKey);
I run npm ls @smithy/util-stream
, here are results,
├─┬ @aws-sdk/client-s3@3.844.0
│ ├─┬ @aws-sdk/middleware-flexible-checksums@3.844.0
│ │ └── @smithy/util-stream@4.2.3 deduped
│ ├─┬ @aws-sdk/middleware-sdk-s3@3.844.0
│ │ └── @smithy/util-stream@4.2.3 deduped
│ └── @smithy/util-stream@4.2.3 deduped
Deleting and regenerating package-lock.json
led to unintended SDK upgrades.
Our package.json contained:
"@aws-sdk/client-s3": "^3.x.x"
So when the package-lock.json file was deleted and re-generated, npm install resolved newer versions that caused problems
How I Solved It
I rolled back to a working package-lock.json. Then, I ran:
rm -rf node_modules/
npm install