3
votes

I'm using aws-sdk module in node.js, and am attempting to validate a config object which contains a profile parameter (sent as a string and used to construct a SharedIniFileCredentials object) and a region.

To validate the sent region, I will create an ec2 object using a known region, and the profile parameter provided above and then check if the region paramter is found in the result of ec2.describeRegions.

I'd like to validate the profile parameter by catching any exceptions thrown executing the above, and am trying this with the setup below:

var aws = require('aws-sdk'); // aws sdk
var creds = new aws.SharedIniFileCredentials({profile: profile});
var conf = new aws.Config({
    "credentials": creds,
    "region": "eu-west-1" //Known region
});
var ec2 = new aws.EC2(conf);
try {
    ec2.describeRegions({}, function (err, data) {
        if (err) throw new Error(err.message);
        //Do stuff below to check if region is found in data.Regions array
        //...
        // If not found -> errorCallback();
        successCallback();
    });
} catch(err){
    errorCallback();
}

It seems that when the profile is invalid, err.message is correctly coming out as:

CredentialsError: Missing credentials in config, if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1

but instead of catching the error constructed from this, the error is unhandled and kills the process:

C:\workspaces\njw\node_modules\aws-sdk\lib\request.js:31 throw err; ^

Error: Missing credentials in config, if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1 at Response. (C:\workspaces\njw\test.js:69:24) at Request. (C:\workspaces\njw\node_modules\aws-sdk\lib\request.js:364:18)

If I don't throw the error inside the describeRegions callback, it will be correctly handled by the catch clause. Any idea why this isn't working inside the callback?

1
You should move from the older, callback-style SDK invocation to the newer, promise-based SDK invocation. For example: const res = await ec2.describeRegions({}).promise().jarmod

1 Answers

1
votes

The problem here is the scope of try/catch block.

Apart from successCallback and errorCallback are not defined in your example and assuming they are valid in our scope and their meaning are the callbacks of a function, this block

try {
  ec2.describeRegions({}, function (err, data) {
    if (err) throw new Error(err.message);
    successCallback();
  });
} catch(err){
  errorCallback();
}

is equivalent to this one

function responseHandler(err, data) {
  if (err) throw new Error(err.message);
  successCallback();
}

try {
  ec2.describeRegions({}, responseHandler);
} catch(err){
  errorCallback();
}

and the thrown Error is not in not in a try/catch block.

Errors returned from asynchronous function accepting callback (this is not a aws-sdk specific) usually are handled returning the error to caller callback, something like:

function myAsyncCheck(done) {
  ec2.describeRegions({}, (err, data) => {
    if (err) return done(err);

    let result, something_bad;
    //Do checks for something bad accordingly the logic you need

    if(something_bad) return done(new Error("Something bad"));

    // Do other stuff to set result

    done(null, result);
  });
});

or, if you need to call ec2.describeRegions in global scope and successCallback and errorCallback are functions in global scope, probably you need to change errorCallback in order to accept an error as parameter, otherwise you can't get error details, but this is really an ugly design pattern.

var ec2 = new aws.EC2(conf);
ec2.describeRegions({}, (err, data) => {
  if (err) return errorCallback(err);

  //Do checks for something bad accordingly the logic you need

  if(something_bad) return errorCallback(new Error("Something bad"));
  successCallback();
});

Hope this helps.