Skip to content

unhandledRejection #2640

Closed
Closed

Description

@stevenvachon

These should be handled automatically, as to reduce boilerplate. A common issue as a result of this is #1128.

process.on('unhandledRejection', (reason, promise) => throw promise);

For web browsers, we could use window.addEventListener.

Activity

  1. ScottFreeCode commented on May 4, 2017

    @ScottFreeCode
    Contributor

    This is definitely something we should add, since the current timeout behavior is quite uninformative and generally promise implementations support this hook; but let's make sure the resulting test failure message indicates that these errors would have been swallowed by the promise in production and should be somehow handled or communicated to the caller for handling (at which point it's easy for the test to communicate the failure back to Mocha without relying on unhandled rejection functions) -- a lot of the questions we get about the current behavior come from lack of awareness of the need for catch.

  2. ScottFreeCode commented on May 4, 2017

    @ScottFreeCode
    Contributor

    One clarification to my earlier comment: I was thinking specifically of unhandled promise rejections -- I believe Mocha already catches uncaught non-promise-swallowed exceptions?

  3. stevenvachon commented on Jun 5, 2017

    @stevenvachon
    Author

    Yes, I think that's true.

  4. changed the title [-]unhandledRejection and uncaughtException[/-] [+]unhandledRejection[/+] on Jun 5, 2017
  5. rogierschouten commented on Dec 4, 2017

    @rogierschouten

    +1 We were completely caught by surprise that mocha catches thrown errors but not unhandled rejections. Our tests passed when actually they should have failed.

    describe("normal test", () => {
      it("bar", () => {
        Promise.reject(new Error("baz"));    
      });
      it("bar", () => {
        // nothing
      });
    });
    

    This is a synchronous test which happens to result in an unhandled rejection (suppose e.g. the code under test had an unintended floating promise). In this case, testing results in "foo" and "bar" succeeding and exit code 0.

    You cannot prevent "foo" from succeeding since node will need a couple of ticks to decide that a promise is unhandled. However I would never expect exit code 0. If necessary, manually go through a couple of node cycles at the end of a mocha execution to wait for any unhandled rejections and then exit with an error message and non-zero code

    Current work-around:

    let unhandledRejectionExitCode = 0;
    
    process.on("unhandledRejection", (reason) => {
    	console.log("unhandled rejection:", reason);
    	unhandledRejectionExitCode = 1;
    	throw reason;
    });
    
    process.prependListener("exit", (code) => {
    	if (code === 0) {
    		process.exit(unhandledRejectionExitCode);
    	}
    });
    
    
  6. boneskull commented on Dec 16, 2017

    @boneskull
    Member

    This is what I think needs to happen:

    1. An unhandled rejection needs to result in the same (or similar) behavior as when an uncaught exception is thrown
    2. A new flag, --allow-unhandled, should be added (if we piggybacked on --allow-uncaught, it would be more difficult to pin down either one in particular)
    3. Plenty of tests.

    Since Mocha doesn't use Promises internally, this shouldn't be a particularly severe change to the codebase. I think we can get away with basically copy-and-pasting the majority of the "uncaught exception" behavior, as well as the test suites.

    There are likely some edge cases here I'm not considering, so this is best-case scenario. 😇

  7. lrowe commented on Jan 20, 2018

    @lrowe
    Contributor

    Not sure if this is a separate issue or not, but I see similar behaviour without promises when deferring an error with setImmediate or process.nextTick.

    // swallow.js
    it("swallows deferred error", function() {
      setImmediate(() => { throw new Error("thrown error"); });
    });

    Running the test I get success with exit code 0.

    $ node bin/mocha swallow.js
    
    
      ✓ swallows deferred error
    
      1 passing (5ms)
    
    

    Whereas running the same code with node v8.9.0 gives prints the stack trace and exits non-zero:

    $ node -e 'setImmediate(() => { throw new Error("thrown error"); });'
    [eval]:1
    setImmediate(() => { throw new Error("thrown error"); });
                         ^
    
    Error: thrown error
        at Immediate.setImmediate [as _onImmediate] ([eval]:1:28)
        at runCallback (timers.js:789:20)
        at tryOnImmediate (timers.js:751:5)
        at processImmediate [as _immediateCallback] (timers.js:722:5)
    
  8. rogierschouten commented on Jan 21, 2018

    @rogierschouten

    @lrowe Your example is expected behaviour - if you have an a-synchonous test then you should use a callback function or a promise-returning function. When you do, it will be ok.

  9. lrowe commented on Jan 21, 2018

    @lrowe
    Contributor

    @rogierschouten even as an async test I see the same behaviour since the error thrown is not part of the promise chain:

    it("swallows deferred error", function(done) {
      setImmediate(() => { throw new Error("thrown error"); });
      done();
    });

    Modifying my example to log when the error is thrown and with logging added to Runner.prototype.run's uncaughtException handler:

    // swallow.js
    it("swallows deferred error", function() {
      setImmediate(() => {
        console.log("throwing error");
        throw new Error("thrown error");
      });
    });

    We see:

    $ node bin/mocha swallow.js 
    
    
      ✓ swallows deferred error
    throwing error
    inside Runner's uncaughtException handler
    
      1 passing (5ms)
    
    removing uncaughtException handler on Runner's end event 
    

    I understand why the test would pass in this instance, but it seems wrong for the mocha Runner.prototype.uncaught to silently // Ignore errors if complete or pending so I would expect the error to be reported and Mocha to exit with a non-zero status.

    My example is a minimal reproducer from using TC39 proposal Observables in a Mocha test. Exceptions raised by the supplied observer methods are specified to report errors asynchronously to the host so that cleanup logic may first be run.

  10. rogierschouten commented on Jan 21, 2018

    @rogierschouten

    @lrowe that is because it's your responsibility to call the done callback only when the test is finished. You're supposed to call it with the error object from within the setimmediate.

  11. rogierschouten commented on Jan 22, 2018

    @rogierschouten

    @lrowe I re-read your post and now I get your point. Sorry for my too quick response

  12. 9 remaining items

  13. stevenvachon commented on Oct 15, 2020

    @stevenvachon
    Author

    @benjamingr and web browsers?

  14. benjamingr commented on Oct 15, 2020

    @benjamingr

    @stevenvachon it doesn't make sense for browsers to exit the process on unhandled rejections in my opinion just like browsers don't exit on uncaught exceptions.

    I would assume (and hope!) that the browser counterpart will never happen and browsers will not crash on unhandled rejections.

    That said: browsers don't (and never did) support the unhandledRejection specification - they support a separate event that is in line with the browser event model (on window and not process, with an Event and so forth).

  15. stevenvachon commented on Oct 15, 2020

    @stevenvachon
    Author

    @benjamingr but Mocha should still fail the same within a web browser; per this thread's original comment.

  16. juergba commented on Jan 29, 2021

    @juergba
    Contributor

    In Mocha v8.2.0 we added an unhandledRejection listener.
    Going to close this issue.

  17. pie-r commented on Mar 3, 2021

    @pie-r

    @juergba To get unhandledRejection in the xxxx.test.ts at the beginning I need to add the following rows in order to grab the Exception raised in any async function.

    process.on('unhandledRejection', (reason) => {
      throw reason;
    });
    

    E.G. In the following example mocha doesn't print anything. Mocha@8.3.0

    private async XXX() {
        throw new Error('fooo error');
    

    It seems Mocha doesn't handle this type of event and silently hides them. Is the expected behavior?

  18. eyalroth commented on Oct 20, 2021

    @eyalroth

    @juergba Please reopen this issue. The faulty behavior was reintroduced in 8.2.1 - #4489

  19. juergba commented on Oct 20, 2021

    @juergba
    Contributor

    @eyalroth You would have to be more precise in order to know what is your "faulty behavior".
    I have not implemented PR4489.

  20. eyalroth commented on Oct 20, 2021

    @eyalroth

    @juergba The same faulty/unwanted behavior described in this issue and fixed in 8.2.0 or earlier; i.e, that mocha silently ignores unhandled promise rejections that occur in tests instead of failing the tests.

  21. the-spyke commented on Nov 11, 2021

    @the-spyke

    Was debugging this issue. All Mocha does on unhandled rejections is just

    process.emit('unhandledRejection', reason, promise);

    This is useless and the issue must be reopen.

    I could try to mitigate this by throwing myself in process.on('unhandledRejection'), which will lead to Mocha going into uncaught route, but it is a hack.

  22. GCSBOSS commented on May 18, 2023

    @GCSBOSS

    I seem to have this same issue in 10.2.0

  23. killagu commented on Aug 3, 2023

    @killagu

    Use this env NODE_OPTIONS=--unhandled-rejections=strict can convert unhandledRejection event to uncaughtException .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    type: buga defect, confirmed by a maintainer

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions