Search code examples
node.jsamazon-web-servicesamazon-s3aws-sam-cliaws-sam

AWS SAM Lambda function to upload to S3 returns invalid response


I'm trying to write a Lambda function to upload a file to S3, using AWS SAM... I'm testing it locally but it looks like nothing is happening and the lambda function ends with invalid response. No other errors are logged. What is going on?

This is the execution log:

START RequestId: 71030098-0dd5-1137-0e78-43f1b1671b9c Version: $LATEST
END RequestId: 71030098-0dd5-1137-0e78-43f1b1671b9c
REPORT RequestId: 71030098-0dd5-1137-0e78-43f1b1671b9c  Duration: 2118.53 ms    Billed Duration: 2200 ms    Memory Size: 128 MB Max Memory Used: 46 MB  
2019-03-27 10:59:00 Function returned an invalid response (must include one of: body, headers or statusCode in the response object). Response received: null
2019-03-27 10:59:00 127.0.0.1 - - [27/Mar/2019 10:59:00] "POST /myfunction HTTP/1.1" 502 -

Lambda code. The file that will be uploaded to S3 comes from the request body.

const async = require('async');
const axios = require('axios');
const AWS = require('aws-sdk');
const s3 = new AWS.S3();

const bucketName = process.env.STRINGS_BUCKET_NAME;

exports.lambdaHandler = async (event, context) => {
    let response;

    try {
        const body = JSON.parse(event.body);
        const jsonFilename = new Date().getTime() + '.json';

        async.waterfall([
            function uploadToS3(done) {
                const base64data = new Buffer(body.strings, 'binary');
                s3.putObject({
                        Bucket: bucketName,
                        Key: jsonFilename,
                        Body: base64data,
                    }, (err, success) => {
                        if (err) {
                            console.error(err);
                            throw Error(err);
                        }
                        console.log(success);
                        console.info('File uploaded to S3: ' + jsonFilename);
                        done(null);
                    });
            },
            function doOtherStuff(done) {
                console.log('doOtherStuff');
                done(null);
            }
        ],
            (error) => {
                if (error) {
                    console.error(error);
                    response = {
                        'statusCode': 500,
                        'body': JSON.stringify({
                            statusCode: 500
                        })
                    };
                } else {
                    response = {
                        'statusCode': 200,
                        'body': JSON.stringify({
                            statusCode: 200
                        })
                    };
                }
            });
    } catch (err) {
        console.error(err);
        return err;
    }

    return response
};

Part of my template.yaml where I defined the lambda function:

Resources:
  UserStringsBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName: 'mybucket'

  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: my-function/
      Handler: app.lambdaHandler
      Runtime: nodejs8.10
      Events:
        MyFunction:
          Type: Api
          Properties:
            Path: /myfunction
            Method: post
      Environment:
        Variables:
          STRINGS_BUCKET_NAME: 'mybucket'
      Policies:
        - AWSLambdaExecute
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - s3:PutObject
                - s3:PutObjectACL
              Resource: 'arn:aws:s3:::mybucket/*' 

Solution

  • It looks like you're getting this error because you're not waiting for the async functions to complete. You call async.waterfall, and then you have your return response line, and because the functions inside the waterfall run async, your return response gets finished first, hence the Function returned an invalid response error.

    To confirm this, you can change your let response; line to let response = {'statusCode': 200, 'body': 'not yet completed'};, and see if you get this response back.