question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Timeout and Promise causing "Uncaught Error" in console

See original GitHub issue

I was unable to reproduce this issue in a command line project, but I was able to using the website HTML example: http://cucumber.github.io/cucumber-js/

I only changed the When part in the sample code, but it happens on Given and Then as well:

this.When(/^I increment the variable by (\d+)$/, function(number, next) {
  this.incrementBy(number);
  setTimeout(() => {
    throw 'this is an uncaught error'
    next()
  }, 1000)
});

Looking at the console when running the feature, I get the message:

Uncaught this is an uncaught error

It never goes to next(), so it take 5 seconds to finish. The same issue happens in Promise.then and also in setInterval. I also tested when returning a Promise and not passing next, but experienced the same issue.

I use Chai as my assertion library, and when I do expect(true).to.be.false, it throws and Assertion Error and normally; that gets picked up through Cucumber which properly fails the test. On the other hand, if I wrap this error in a setTimeout, setInterval, or Promise, then the Uncaught error message shows up in the console instead of Cucumber picking up on the error.

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:8 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
Sawtaytoescommented, Oct 21, 2016

I see, so I could do something like this:

asyncAssert = function(expectation, next) {
    try {
        expectation();
    } catch(err) {
        next(err.message);
    }
};

And wrap my statement like so:

asyncAssert(() => expect(err).to.not.exist, next);

Or I could use the much easier method without next:

this.Given(/^this should work$/, () => {
    return Promise.resolve()
    .then(() => {
        throw "This error is caught by cucumber.";
    });
});

I get it now. Cucumber has a promise handler which will catch any errors that I’m not already trying to catch in my Promise. I was able to break Cucumber’s error handling by adding a .catch to the promise:

this.Given(/^this should work$/, () => {
    return Promise.resolve()
    .then(() => {
        throw "This error is not caught by cucumber.";
    })
    .catch(() => console.log("I'm breaking cucumber!"));
});

And that’s how it should work since I’m specifically catching this error before sending it to Cucumber. If I returned a Promise.reject(err) in the .catch, then Cucumber could pickup that error as it’s now “uncaught” by my code:

this.Given(/^this should work$/, () => {
    return Promise.resolve()
    .then(() => {
        throw "This error is caught by cucumber.";
    })
    .catch(err => {
        console.log("I'm breaking cucumber!");
        return Promise.reject(err);
    });
});

I also verified it works with jQuery’s legacy promise (before version 3):

this.Given(/^this should work$/, () => {
    const deferred = $.Deferred();
    deferred.resolve();
    return deferred.promise()
    .done(() => {
        throw "This error is caught by cucumber.";
    });
});

Which means there’s a try-catch surrounding the execution of this.Given. So when you have an error in a setTimeout or Promise, there’s no way for Cucumber to know if there’s an exception thrown unless you explicitly tell it using next('Didn't pass assertion!'), or you give it access to the promise pointer and let the try-catch in Cucumber handler it.

Now I get it thanks!

0reactions
lock[bot]commented, Oct 25, 2018

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Avoid uncaught exception when a promise gets rejected due ...
For me, the debugger halts in the rejectAfterDelay function with the message “Exception has occurred: timeout” at the line reject(“timeout”). – ...
Read more >
Error handling with promises
Asynchronous actions may sometimes fail: in case of an error the corresponding promise becomes rejected. For instance, fetch fails if the remote server...
Read more >
Process | Node.js v19.3.0 Documentation
The 'rejectionHandled' event is emitted whenever a Promise has been rejected and an error handler was attached to it (using promise.catch() , for...
Read more >
Using promises - JavaScript - MDN Web Docs
Unterminated promise chains lead to uncaught promise rejections in most browsers. See error handling below. A good rule of thumb is to always ......
Read more >
25. Promises for asynchronous programming - Exploring JS
That is, uncaught errors are passed on until there is an error handler. ... function timeout ( ms , promise ) { return...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found