I'm trying to debug a problem that I think is being caused by an uncaught error in an async process. This is simple example that simulates the type of error I'm trying to detect:
async function test(event, context) {
console.log('I can find this log message');
context.callbackWaitsForEmptyEventLoop = false;
setTimeout(() => {
console.log('I cannot find this one');
throw new Error('uh oh'); // I don't see this in CloudWatch, either
}, 1000);
return {};
}
I know the lambda is frozen after the function returns, so I'm guessing there's no way to see these errors/logs.
Is there anything I can do to try to detect what is still in the event loop when the handler returns so I might be able to track down an unhandled error like this one?
You can use subscribe to the unhandled rejections.
process.on('unhandledRejection', error => { // log error});
hope this helps.
Related
I've got an Api call that is converted to a promise. My handleError function inside the observable re-throws via throwError. This re-thrown error does not trigger any catch in the outer Promise chain.
callApi() {
return this.http.get(`${this.baseUrl}/someapi`)
.pipe(
map((data: any) => this.extractData(data)),
catchError(error => this.handleError(error))
).toPromise();
handleError(error) {
console.error(error);
return throwError(error || 'Server error');
}
Calling code...
this.someService.callApi()
.then((response) => {
// THIS GETS CALLED AFTER throwError
// do something cool with response
this.someVar = response;
})
.catch((error) => {
// WE NEVER GET TO HERE, even when I force my api to throw an error
console.log(`Custom error message here. error = ${error.message}`);
this.displayErrorGettingToken();
});
Why doesn't the throwError trigger the Promise catch?
You should not use toPromise() when possible.
Use subscribe instead of then.
Also when you catch the error in a pipe it won't be thrown in then because you already caught it, also when you throw the error in a catch error, it won't be emitted into the regular pipe flow of your response.
callApi() {
return this.http.get(`${this.baseUrl}/someapi`);
}
This is totally ok. Http.get() returns a singleton observable stream, which emits only ONE value and then completes. Subscribe to the Observable.
this.someService.callApi()
.subscribe((response) => {
// THIS GETS CALLED always wenn everything is ok
this.someVar = response;
},
(error:HttpErrorResponse) =>{
console.log(`Custom error message here. error ${error.message}`);
this.displayErrorGettingToken();
});
Observable is like an extended version of promise. Use it.
I'm getting an unhandled promise rejection error when I use useMutation with react native. Here's the code producing the issue:
const [createUser, { error, loading }] = useMutation(CREATE_USER_MUTATION);
Anytime my graphql server returns an error to the client I get an unhandled promise rejection error (screenshot below). I'm able to make it go away by adding an error handler like so, but it seems like a hack. Any thoughts? Am I doing something wrong or is this something that should be addressed by the apollo folks?
const [createUser, { error, loading }] = useMutation(CREATE_USER_MUTATION, {
onError: () => {}
});
Your createUser mutation is a promise you should handle error inside try catch block, or in upper scope inside apollo-link-error onError method.
const [createUser, { data, loading }] = useMutation(CREATE_USER_MUTATION, {
onError: (err) => {
setError(err);
}
});
With this we can access data with loading and proper error handling.
I'm trying to throw exception in the async code. If I throw it directly like this, it works fine. Example:
Query: {
requisitions: async (parent, args, { req }, info) => {
const user = await Auth.findOne({ _id: req.session.userId }).select('roles')
if (!user.roles.some(role => ['driver', 'requestant'].includes(role))) {
throw new ForbiddenError(ErrorCodes.UNAUTHENTICATED)
}
return true
}
}
But if I make a helper function to handle this, it doesn't work. I need the helper function because this roles check is needed at many places in the app.
Query: {
requisitions: async (parent, args, { req }, info) => {
hasRights(req, ['driver', 'requestant'])
return true
}
}
export const hasRights = async (req, allowedRoles) => {
const user = await Auth.findOne({ _id: req.session.userId }).select('roles')
if (!user.roles.some(role => allowedRoles.includes(role))) {
throw new ForbiddenError(ErrorCodes.UNAUTHENTICATED)
}
}
Note: the hasRights is exported from another file.
I have tried all combinations of try/catch blocks in both files but I just get the UnhandlePromiseRejectionWarning in the server console but the requisitons resolver continues in execution. I want it to stop if the exception is thrown. I have no idea why it only works one way and not the other, even though it is the same thing. Any help appreciated. Thanks.
Calling hasRights(req, ['driver', 'requestant']) can return a rejected promise, but you have no code to ever handle that. As such, it's an unhandled rejection.
When the throw is inline, it's caught by the async requsitions() function which then returns a rejected promise so the caller of requisitions() has a chance to catch it.
You can make sure a rejected promise returned from hasRights() is handled the same way by adding await in front of the hasRights() call like this:
Query: {
requisitions: async (parent, args, { req }, info) => {
await hasRights(req, ['driver', 'requestant'])
return true
}
}
This will allow the async requisitions() function to automatically catch a rejection from hasRights() the same way your original inline throw did.
You will, of course, have to make sure you handle rejected promises properly in the callers of requisitions().
It also occurs to me that you need to know that executing a throw inside an async function will be automatically caught by the async function and converted into a rejection of the promise that is returned by the async function. This is a feature of async functions.
I'm trying to handle an error in Cypress but the Cypress app is throwing error
cy.get('button[name="continue"]',{timeout: 30000})
.catch((err) => {
cy.console.log("error caught");
})
The error I get:
TypeError: cy.get(...).catch is not a function
tl;dr
Cypress has no .catch command the error message clearly states that.
Exception handling in Cypress
The documentation on error recovery clearly states:
The following code is not valid, you cannot add error handling to Cypress commands. The code is just for demonstration purposes.
cy.get('button').contains('hello')
.catch((err) => {
// oh no the button wasn't found
// (or something else failed)
cy.get('somethingElse').click()
})
They deliberately left this out and in the docs they explain it in great length why you shouldn't be able to do it.
If you really want, you can catch uncaught exceptions, just try the suggestions of the Catalog of Events on this matter:
it('is doing something very important', function (done) {
// this event will automatically be unbound when this
// test ends because it's attached to 'cy'
cy.on('uncaught:exception', (err, runnable) => {
expect(err.message).to.include('something about the error')
// using mocha's async done callback to finish
// this test so we prove that an uncaught exception
// was thrown
done()
// return false to prevent the error from
// failing this test
return false
})
// assume this causes an error
cy.get('button').click()
})
I have a lambda function that is invoked when a cognito user submits their verification code.
PostConfirmation failed with error RequestId: 3241xxxx-3cxx-11xx-aexx-8b39059xxxxx Process exited before completing request.
at constructor.e (<anonymous>:21:4685)
at constructor.callListeners (<anonymous>:21:24558)
at constructor.emit (<anonymous>:21:24267)
at constructor.emitEvent (<anonymous>:21:18671)
at constructor.a (<anonymous>:21:14521)
at d.runTo (<anonymous>:22:12444)
at <anonymous>:22:12651
at constructor.<anonymous> (<anonymous>:21:14731)
at constructor.<anonymous> (<anonymous>:21:18726)
at constructor.callListeners (<anonymous>:21:24664)
And my lambda function's job is to receive that submission and publish a message to SQS, and then continue.
module.exports.trigger_userVerified = (event, context, callback) => {
const AWS = require('aws-sdk');
var nickname = event.request.userAttributes.nickname;
var params = {
MessageBody: 'A user has registered an account and has supplied a website address.',
QueueUrl: ' https://sqs.us-east-1.amazonaws.com/xxx/my_sqs_queue',
DelaySeconds: 0,
MessageAttributes: {
'nickname': {
DataType: 'String',
StringValue: nickname
}
}
};
sqs.sendMessage(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
context.done(null, event);
});
};
Is there something wrong with the way I'm handling this lambda function that causes this error? how do I investigate further?
The error "Process exited before completing request" means that the lambda Javascript function exited before calling context.done (or context.succeed, etc.). Most of the times this means that there is an error in the function.
I'm not a Javascript expert (at all) so there may be more elegant ways to find the error but my approach has been to put a bunch of console.log messages in my code, run it, and then look at the logs. I can usually zero in on the offending line and, if I look at it long enough, I can usually figure out my mistake.
Since it's lambda:
Please check your cloudwatch logs and see if you can find more. In your place I would add some console.log statements as well.