Search code examples
node.jsamazon-s3aws-sdklocalstack

AWS S3 giving error "exception during call chain: Unable to parse request (not well-formed (invalid token): line 1, column 0), invalid XML received:"


I am using node.js to upload image to my s3 bucket on my local environment using localstack

here is my API code:

const s3 = new AWS.S3({
  accessKeyId: 'testKEYId',
  secretAccessKey: 'testSecret',
  region: 'ap-south-1',
  sslEnabled: false,
  endpoint: 'http://localhost:4566',
});

app.post('/upload-1', upload.any(), (req, res) => {
  const imagePath = './myfolder/my-image.jpg'; // Update with your image file path
  const bucketName = 'my-buckert'; // Update with your S3 bucket name
  const remoteFileName = 'uploaded_image.jpg';

  const fileContent = fs.readFileSync(imagePath);

  console.log(fileContent);

  const params = {
    Bucket: bucketName,
    Key: remoteFileName,
    Body: fileContent,
  };

  const bucketParams = {
    Bucket: bucketName,
  }

  s3.upload(params, (err, data) => {
    if (err) {
      console.error('Error uploading image to S3:', err);
    } else {
      console.log('Image uploaded successfully. S3 location:', data.Location);
    }
  });
});

when i am calling this API then i am getting this error:

exception during call chain: Unable to parse request (not well-formed (invalid token): line 1, column 0), invalid XML received:

Refer screenshot: enter image description here


Solution

  • This issue stems from using Virtual-Hosted style requests against LocalStack when using localhost as the endpoint.

    Your SDK is prepending the bucket to your host which leads to a request looking like this:
    http://my-buckert.localhost:4566/uploaded_image.jpg
    LocalStack is not able to parse the bucket name from your host if the bucket name is is not followed by .s3., and will treat this request as a CreateBucket call.

    There are two solutions to this, outlined in the documentation here: https://docs.localstack.cloud/user-guide/aws/s3/#path-style-and-virtual-hosted-style-requests

    In your case, the simplest would be to use s3ForcePathStyle for your client. Creating your client this way should fix your issue:

    const s3 = new AWS.S3({
      accessKeyId: 'test',  // I would advise to use `test` and `test` for the keys
      secretAccessKey: 'test',
      region: 'ap-south-1',
      sslEnabled: false,
      endpoint: 'http://localhost:4566',
      s3ForcePathStyle: true,
    });
    

    More about the configuration of the javascript AWS SDK here: https://docs.localstack.cloud/user-guide/integrations/sdks/javascript/