Search code examples
javascriptamazon-web-servicesamazon-s3aws-sdk-js

AWS S3 headObject not returning all metadata values in JS SDK


I have an object in an AWS S3 bucket with some metadata keys set on it. I fetch the keys and values by using headObject() in the AWS v2 JavaScript SDK.

const headParams = {
    Bucket: "THE_BUCKET", 
    Key: "THE_KEY"
};

S3.headObject(headParams, (err, data) => {
    if(err) {
        console.log(err);
    } else {
        console.log(data);
    }
});

If I then "modify" one of those by keys using copyObject():

objectMetadata["SOME_EXISTING_METADATA_KEY"] = "Some New Value";

const params = {    
    Bucket: "THE_BUCKET", 
    Key: "THE_KEY",
    CopySource: "THE_BUCKET/THE_KEY",
    MetadataDirective: "REPLACE",
    ACL: "public-read",
    'Metadata': objectMetadata
};

S3.copyObject(params, (err, data) => {
    if(err) {
        console.log(err);
    } else {
        console.log(data);
    }
});

And then call headObject() again, I get the updated value, as expected.

If I use the same copyObject() code above, but try to add a new metadata key that does not already exist on the object:

...
objectMetadata["SOME_NEW_METADATA_KEY"] = "Some New Value";
...

I see the new value in the S3 console, but when I call headObject() again, the new metadata key does not appear.

However, the timestamp in the LastModified header shows that the object was modified when I added the new key. And, if I use the AWS CLI to retrieve the metadata for the same object, the new key does appear:

aws s3api head-object --bucket THE_BUCKET --key THE_KEY

{
    "AcceptRanges": "bytes",
    "LastModified": "2021-05-03T19:06:14+00:00",
    "ContentLength": 18340648,
    "ETag": "\"a8...\"",
    "ContentType": "application/octet-stream; charset=UTF-8",
    "Metadata": {
        ...
        "SOME_NEW_METADATA_KEY": "Some New Value",
        ...
    }
}

Has anyone seen this behavior before? I can't figure out why the key would appear in the console and in the results of the CLI, but not the JavaScript SDK. Especially since the SDK shows the results of updating existing keys just fine.


Solution

  • One of my coworkers was able to track this down. In case anyone else comes across this issue, it turned out to be a CORS configuration problem*.

    Each metadata key needs to be explicitly added as an ExposeHeader entry in the CORS configuration.

    Here's the relevant section in the CORS configuration, which you can edit under the Permissions tab in S3, in the "Cross-origin resource sharing" section:

        "ExposeHeaders": [
            ...
            "SOME_EXISTING_METADATA_KEY",
            "SOME_NEW_METADATA_KEY"
        ]
    

    * Should have known. As my grandfather was fond of saying, "When something on the web doesn't work right, it's probably a CORS problem."