How can i trigger error object in lambda.invoke
lambda.invoke(params, (err, data) => {
if (err) {
reject(.... // I would have expected below error to show up here
else
// error shows up inside the data.Payload
const result = data.Payload
// I have to create a condition to check for the error
resolve(result);
in the called lambda, i've tried the following:
exports.handler = ( event, context, callback) => {
if (payload === '')
context.done(new Error('my error message');
}
however, the error object ends up in the payload, where I have to check for it instead of going into a catch or other error path.
Instead of using the "older way" to stop execution you should use the callback(error, [success]) method. So in your lambda being invoked try callback('my error message') and that should go into your if block. You can read the documentation here for more info. I believe that in the "older way" of doing things context.done() is considered successful and context.fail() was used to signify an error.
Related
I have the following call to an API (an npm module running in Node.js) in a JavaScript file in which I would like to catch all errors so can gracefully handle them. But if I e.g. pass a bad API-KEY or a city name that does not exist, there is an error in the internal code of the API which is not caught by the try/catch:
const weather = require('openweather-apis');
const getTemperature = (city, cbSuccess, cbFailure) => {
try {
weather.setLang('de');
weather.setCity(city);
weather.setUnits('metric');
weather.setAPPID('BADKEY');
weather.getTemperature((err, temperature) => {
if (err) {
console.log(err);
} else {
console.log(`The temperature in ${city} is ${temperature}° C.`);
}
});
} catch (error) {
console.log('there was an error');
}
}
getTemperature('Berlin');
Rather, an error is displayed and execution stops:
C:\edward\nwo\jsasync\node_modules\openweather-apis\index.js:162
return callback(err,jsonObj.main.temp);
^
TypeError: Cannot read property 'temp' of undefined
at C:\edward\nwo\jsasync\node_modules\openweather-apis\index.js:162:40
at IncomingMessage.<anonymous> (C:\edward\nwo\jsasync\node_modules\openweather-apis\index.js:250:18)
at IncomingMessage.emit (events.js:194:15)
at endReadableNT (_stream_readable.js:1125:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
Is there a way in JavaScript to catch all errors as one does in e.g. Java and C#?
I believe that something like this might work:
async execute(weather, city, temperature) {
return await new Promise(function(resolve, reject) {
weather.getTemperature((err, temperature) => {
if (err) {
reject(err);
} else {
resolve(`The temperature in ${city} is ${temperature}° C.`);
}
});
};
}
const getTemperature = async (city, cbSuccess, cbFailure) => {
try {
weather.setLang('de');
weather.setCity(city);
weather.setUnits('metric');
weather.setAPPID('BADKEY');
const res = await execute(weather, city, temperature);
console.log(res);
} catch (error) {
console.log('there was an error');
}
}
You're out of luck if an exception throws in asynchronous code. This will stop execution of the script (as you're seeing above).
The module you are using should possibly handle the error in a better way and pass the error in the callback err parameter. Unless you fork the code or file a bug you're stuck with this.
The same effect can be demonstrated here:
async function testAsyncException() {
try {
setTimeout(() => {
throw new Error("Error in asynchronous code");
}, 100);
} catch (e) {
// This will never be caught...
console.error("testAsyncException: A bad error occurred:", e);
}
}
process.on('uncaughtException', (e) => {
console.log("uncaughtException:", e);
})
testAsyncException();
The try .. catch block around the setTimeout call will not handle the generated exception.
The only way you can "catch" this type of exception is using a process event like so:
process.on('uncaughtException', (e) => {
console.log("uncaughtException:", e);
})
This however should only be used to log and then exit. Trying to recover program state at this point is not a good idea, since the application is in an unknown state.
If you're using a process manager such as the very useful PM2, the script can be automatically restarted on errors.
Conversely if we try the following:
function testSyncException() {
try {
throw new Error("Error in synchronous code");
} catch (e) {
// This will be caught...
console.error("testSyncException: A bad error occurred:", e);
}
}
testSyncException();
We can see that the exception will be caught.
I strongly recommend this excellent article on error handling by the creators of Node.js (Joyent):
https://www.joyent.com/node-js/production/design/errors
It details the best strategies for handling both Operational errors and Programmer errors.
there is an error in the internal code of the API
return callback(err,jsonObj.main.temp);
^
TypeError: Cannot read property 'temp' of undefined
at C:\edward\nwo\jsasync\node_modules\openweather-apis\index.js:162:40
This is clearly a bug in the openweather-apis library. Report it. You hardly will be able to work around it. The library will need to check whether jsonObj and jsonObj.main exist before attempting to access .temp on it, and it should call your callback with an error if the jsonObj doesn't look as expected.
Consider the following example Lambda function:
//get-account
exports.handler = (data, context, callback) => {
callback("Account not found");
};
Lambda would output this error like so:
{
"errorMessage": "Account not found"
}
This is exactly what I want. However, consider then calling this function via the AWS API
return new Promise((resolve, reject) => {
Lambda.invoke({
FunctionName: 'get-account',
InvocationType: "RequestResponse",
Payload: JSON.stringify({ account_id: account_id })
}, function(err, data) {
//only true if the invocation failed...
if(err) { return reject(err); }
//parse the payload. We will need it regardless of error/success
let response = JSON.parse(data.Payload);
//did the function throw an error?
if(data.FunctionError) {
//the error message will be [object Object], which is no good!
reject(response.errorMessage);
return;
}
//success!
resolve(response);
});
});
In this case, I find the error is always converted to the [object Object] string. Lambda appears to take the error object from the callback (the error object it creates) and then wraps it in another error object. So ultimately Lambda.invoke is doing this:
{
errorMessage: {
errorMessage: "Account not found"
}
}
But instead of returning this built object, it returns,
{
errorMessage: '[object Object]'
}
Anyone see a way around this? I do NOT want to change how my Lambda function outputs errors. I only want to get the correct error from the Lambda invoke function. Is this simply not possible due to how Lambda.invoke() wraps the error again?
This problem might seem like a duplicate of many others, but I can't find my mistake anywhere.
The problem is that async.each throws "Callback was already called". Here's the snippet (I named the async callback done, so it doesn't get confused with the other callbacks in my code):
async.each(this.requirements, (requirement, done) => {
// That thing here passes the result as a callback
requirement.callback((result) => {
if (!result) {
// requirement not passed -> return error
done(true); // LINE 42
} else {
done(); // LINE 44
}
}, data, params, bot);
}, (err) => { // 'done' callback
log.info('handler',
`Handler '${this.label}' ${err ? 'failed' : 'succeeded'}`);
// if any requirement did not pass, do not execute handler callback
if (!err) this.callback(data, params, bot);
});
Here's the stack trace of it:
C:\Users\samuel\Code\node\sk22tgjs\node_modules\async\dist\async.js:837
if (fn === null) throw new Error("Callback was already called.");
^
Error: Callback was already called.
at C:\Users\samuel\Code\node\sk22tgjs\node_modules\async\dist\async.js:837:34
at requirement.callback (C:\Users\samuel\Code\node\sk22tgjs\node_modules\telegramjs\core\handler.js:44:11)
at Requirement.exports.command.Requirement.callback (C:\Users\samuel\Code\node\sk22tgjs\node_modules\telegramjs\telegram\requires.js:21:5)
at async.each (C:\Users\samuel\Code\node\sk22tgjs\node_modules\telegramjs\core\handler.js:39:19)
at C:\Users\samuel\Code\node\sk22tgjs\node_modules\async\dist\async.js:2953:18
at replenish (C:\Users\samuel\Code\node\sk22tgjs\node_modules\async\dist\async.js:872:19)
at C:\Users\samuel\Code\node\sk22tgjs\node_modules\async\dist\async.js:878:27
at C:\Users\samuel\Code\node\sk22tgjs\node_modules\async\dist\async.js:840:18
at requirement.callback (C:\Users\samuel\Code\node\sk22tgjs\node_modules\telegramjs\core\handler.js:44:11)
at Requirement.callback (C:\Users\samuel\Code\node\sk22tgjs\node_modules\telegramjs\core\requires.js:19:5)
The interesting thing is that the issue only occurs if done(true) gets called. Nevertheless, the error occurs at line 44, not 42.
You can also see the corrupt code on GitHub, especially the testing branch: https://github.com/22sk/telegramjs
Thanks in advance.
When a command isn't found, both the success and failure are being called rather than just the failure.
https://github.com/22sk/telegramjs/blob/5b85f04fe890a8fd32b373edb97bfebc923156b1/bot/telegram/requires.js
exports.command = new Requirement({
label: 'command',
requires: requires.has('message', 'text'),
callback: (result, data, params, bot) => {
const command = new Command(data.message.text);
if (!command.valid || command.bot && bot.me.username !== command.bot) {
// command is not valid or not meant to be handled by this bot
result(false); // <--- ### Missing return ### --->
}
// command is valid and should be handled by this bot
// write command into data
params.command = command;
result(true);
}
});
Is there any way in node.js to log all exceptions?
process.on('uncaughtException') is not enough for me, because I need to log all caught and uncaught exceptions, even if there was a catch somewhere in the code which just ignored/swallowed the error.
Do you guys think, it is possible in node.js?
One hacky way to do this is using debug context:
const vm = require('vm');
const Debug = vm.runInDebugContext('Debug'); // Obtain Debug object
Debug.setListener((type, _, e) => { // listen for all debug events
if (type == Debug.DebugEvent.Exception) {
console.log(e.exception().stack) // e is an event object
}
});
Debug.setBreakOnException(); // this is required for Exception event to fire
try {
throw new Error('bla');
} catch(e) {
// ha
}
Warning: don't leave this code in production, use for debugging only.
Obviously, it won't call asynchronous errors, because they are not actually thrown, they are just created to passed to a callback.
Another way is to replace possible error constructors:
const OldError = Error;
const MyError = function(message) {
const err = new OldError(message);
OldError.captureStackTrace(err, MyError); // remove top frame from stack trace
console.log(err.stack);
return err;
}
MyError.prototype = Error.prototype; // Fix instanceof
global.Error = MyError;
try {
throw new Error('bla');
} catch(e) {
}
new Error('blabla');
This way you can also handle asynchronous error, but won't see if something other than instance Error is thrown.
If you are interested only in promises and you are using native v8 promises, then you can try this:
const vm = require('vm');
const Debug = vm.runInDebugContext('Debug');
Debug.setListener((type, _, e) => {
if (type == Debug.DebugEvent.PromiseEvent) {
if (e.status() === -1) { // 0=pending, 1=resolved, -1=rejected
console.log(e.value().value().stack);
}
}
});
Promise.reject(new Error('test'))
.catch(() => {});
It will likely generate some duplicates, since it catches child promise rejection as well as original promise rejection.
You could attach a debugger like node-inspector and active the option in node-inspector. This does not log exceptions but pause execution which should be enough to find the quirks in the 3rd party module.
If you're using WebStorm you can log uncaught exceptions to the console or to a file. After starting the WebStorm debugger open the breakpoints dialog and activate the "Any exception" setting for "JavaScript Exception Breakpoints" and breakpoint actions according to
If you are swallowing the exceptions you cannot track them.
If you think that a module that you're using is ignoring the exceptions you are using a bad module or you are not using it correctly.
If you are using Express the correct approach will be to redirect all the exception and errors with next(err).
The exceptions will be passed to the error handler (note the four parameters in the function) and there you can log them:
router.get('/', function (req, res, next) {
// your logic
if(err) {
return next(err);
}
return next();
});
// Error handler
app.use(function(err, req, res, next) {
console.log(err.stack);
res.status(err.status || 500).json({
error: {
code: err.code,
message: err.message
}
});
next(err);
});
The question:
I'm using Chai to do the tests and I seem to be stuck on testing an expected error:
Chai expected [Function] to throw an (error)
Current code:
Here's the code of the test:
describe('Do something', function () {
it('should remove a record from the table', function (done) {
storage.delete(ID, done);
});
it('should throw an error when the lookup fails', function () {
expect(storage.delete.bind(storage, ID)).to.throw('Record not found');
});
});
Here's the code of the function:
delete: function (id, callback) {
// Generate a Visitor object
visitor = new Visitor(id);
/* Delete the visitor that matches the queue an
cookie provided. */
tableService.deleteEntity(function (error, response) {
// If successful, go on.
if (!error) {
// Do something on success.
}
// If unsuccessful, log error.
else {
if (error.code === 'ResourceNotFound') {
throw new Error('Record not found');
}
// For unexpected errros.
else {
throw new Error('Table service error (delete): ' + error);
}
}
if (callback) callback();
});
},
Attempted solutions:
I've tried multiple variations of calling expect function (including calling anonymous function:
expect(function() {storage.delete(ID);}).to.throw('Record not found');
Bind, as provided in the example,
and the basic one of
expect(storage.delete(ID)).to.throw('Record not found');
I've also tried substituting the throw parameter from 'Record not found' to multiple things including directing the input to an already created error (Error), and creating a new error in the parameter (new Error('Record not found'));
Possible causes:
I have a suspicion that the error is not being thrown because it takes a while for the test to communicate with the database to delete the record, however I am not sure of how I could remedy that.
Additionally, it seems that the test that runs right after this one actually returns the error that was supposed to be returned on THIS test.
Given (from comments) that tableService.deleteEntity is asynchronous, it is impossible to test that throw. And the code itself is invalid. Because the thrown exception won't be caught, it will be unhandled as it was thrown in a different tick. Read more about Asynchronous error handling in JavaScript and unhandled exceptions in Node.js
In other words such a function cannot be tested for throwing errors:
function behaveBad(){
setTimeout(function(){
throw new Error('Bad. Don\'t do this');
}, 50);
}