Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"DeprecationWarning: Unhandled promise rejections are deprecated" #5311

Closed
gaearon opened this issue Jan 14, 2018 · 25 comments
Closed

"DeprecationWarning: Unhandled promise rejections are deprecated" #5311

gaearon opened this issue Jan 14, 2018 · 25 comments
Labels

Comments

@gaearon
Copy link
Contributor

gaearon commented Jan 14, 2018

This might be the same issue as #3251 but I'm not sure so posting it separately.

With Jest 22.0.6, no config, this test:

test("I'm asynchronous", async () => {
  const promise = Promise.reject("boom!")
  expect("some precondition").toBeFalsy()
  await rejected(promise)
  expect("some postcondition").toBeTruthy()
})

async function rejected(promise) {
  try {
      await promise
  } catch (e) {
      return e
  }
  throw new Error('Expected promise to be rejected')
}

(taken from an unrelated CRA issue)

produces this log:

(node:33041) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): boom!
(node:33041) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
 FAIL  ./test.js
  ✕ I'm asynchronous (6ms)

  ● I'm asynchronous

    expect(received).toBeFalsy()
    
    Expected value to be falsy, instead received
      "some precondition"

      2 |   const promise = Promise.reject("boom!")
      3 | 
    > 4 |   expect("some precondition").toBeFalsy()
      5 | 
      6 |   await rejected(promise)
      7 | 
      
      at Object.<anonymous>.test (test.js:4:31)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        0.759s
Ran all test suites.

Note these two lines:

(node:33041) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): boom!
(node:33041) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

The first line is being discussed in #3251 with a feature request of making the logging configurable. (There is also a separate report in #3251 (comment) of catch() failing to attach error handlers when it’s too late.)

But I’m worried about the second line. Does it mean that when Node changes behavior as it advertises in the deprecation warning, all tests that print this message will start crashing the process? Or can I safely ignore the warning without worrying that my test suite will be very painful to upgrade to future Node versions when the behavior is flipped? Is there anything Jest should be doing to safeguard my test suite from crashing the test runner in the future, and is this a valid concern at all?

I'm using Node 8.9.1 on macOS Sierra.

@SimenB
Copy link
Member

SimenB commented Jan 15, 2018

I think the issue here is that we clean up our unhandledRejection handler when the test exits, which means this error bubbles up all the way to node.

Not sure how to best handle it. I think maybe @aaronabramov's original suggestion in #3251 (comment)

Or we could try to be smarter about attaching and detaching the listener, similar to how you solved #4767.

@HeroProtagonist
Copy link

I noticed something that I believe might be related to this. I ejected from create-react-app to get to the testing scripts.

// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => {
  throw err;
});

That works and exits the test suite if I add a promise rejection into a test. When I upgrade Jest from 20.0.4 -> ^22.1.4 the promise warning is displayed and test suite successfully goes into watch mode. I would think jest would still fail as in the older version that CRA has.

@gaearon
Copy link
Contributor Author

gaearon commented Feb 3, 2018

I don't think our intention was to exit the test suite btw (that sounds pretty annoying).

The only reason we added that line is to make sure errors in our scripts (not in the actual tests!) crash early.

@HeroProtagonist
Copy link

@gaearon But if what you are asking about is the case

Does it mean that when Node changes behavior as it advertises in the deprecation warning, all tests that print this message will start crashing the process?

Then wouldn't it be desired to crash the suite. Since on a remote it would crash the whole server? I'll need to google and see if in the future uncaught rejections will truly exit the process. It reads that way

@subhashekkaluru
Copy link

D:\ANGUALRJS2 PROJECTS\PR02>ngh
An error occurred!

(node:4020) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: Unspecified error (run without silent option for detail)
(node:4020) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

@SimenB
Copy link
Member

SimenB commented Apr 23, 2018

Same issue throwing an error asynchronously, see #5911 for a reproduction

@grrowl
Copy link

grrowl commented May 15, 2018

edit: Actually this workaround doesn't work. expect(pending).resolves.toBe will fail on the rejection reason, but expect(pending).resolves.toEqual seems to not wait 😕. sorry!

post You can instruct Jest to wait for the promise to resolve using `expects().resolves`, which means Jest won't pack up the unhandledRejection handler until that promise has resolved or rejected:
test("I'm asynchronous", () => { // not async
  const promise = Promise.reject("boom!")
  expect("some precondition").toBeFalsy()
  const pending = rejected(promise) // Assign to variable if necessary
  expect("some postcondition").toBeTruthy()
  expect(pending).resolves.toEqual(expect.anything()) // Wait for promise to resolve
})

async function rejected(promise) {
  try {
      await promise
  } catch (e) {
      return e
  }
  throw new Error('Expected promise to be rejected')
}

Since we didn't use async tests extensively on our project, this was a simple addition and unblocks our migration to Jest 22. It might be more of a pain if your tests rely on async/await extensively.

edit: realised the underlying is Jest's incorrect handling of async tests — this workaround doesn't use them; hopefully it's still a useful workaround until this is resolved.

@dildarmohammad
Copy link

(node:8040) [DEP0018] DeprecationWarning: Unhandled promise rejections are depre
cated. In the future, promise rejections that are not handled will terminate the
Node.js process with a non-zero exit code.

@Femina
Copy link

Femina commented Jul 24, 2018

Hope this will help you to understand the scene.

https://spion.github.io/posts/why-i-am-switching-to-promises.html

@nathany
Copy link

nathany commented Oct 9, 2018

Does it mean that when Node changes behavior as it advertises in the deprecation warning, all tests that print this message will start crashing the process?

Wouldn't you want a UnhandledPromiseRejectionWarning to be treated as a test failure?

In a previous project using Mocha, I set up make-promises-safe to fail the build when promise rejections weren't being caught. Unfortunately I haven't yet figured out how to get that working with Jest, likely because Jest has its own handler for promise rejections.

Personally I dislike seeing warnings like this with no line numbers to go resolve the issues -- and because it doesn't fail the build, a bug could have been introduced long ago without noticing. Is there any way to make this better?

(node:31851) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'account' of undefined
(node:31851) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:31851) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

@phil-lgr
Copy link

Ouch, this is forcing me to put

return Promise.reject(...);

in my application code... not sure what to do

if you try to debug this:

it(`should not produce unhandled Errors`, async () => {
            async function rejected(throwMethod: 'reject' | 'throw' | null) {
                try {
                    if (throwMethod === 'reject') {
                        return Promise.reject(Error('error from Promise.reject'));
                    }
                    if (throwMethod === 'throw') {
                        throw Error('error from throw');
                    }
                    return await fetch('hello');
                } catch (e) {
                    throw e;
                }
            }

            try {
                await rejected('reject');
            } catch (error) {
                console.log(error);
                expect(error).toHaveProperty('message', 'error from Promise.reject');
            }

            try {
                await rejected('throw');
            } catch (error) {
                console.log(error);
                expect(error).toHaveProperty('message', 'error from throw');
            }
        });

you get an unhandled error that's coming from throw Error('error from throw');, but the JavaScript code is perfectly valid, if you paste this in Chrome's console:

(async () => {
            async function rejected(throwMethod) {
                try {
                    if (throwMethod === 'reject') {
                        return Promise.reject(Error('error from Promise.reject'));
                    }
                    if (throwMethod === 'throw') {
                        throw Error('error from throw');
                    }
                    return await fetch('hello');
                } catch (e) {
                    throw e;
                }
            }

            try {
                await rejected('reject');
            } catch (error) {
                console.log(error);
            }

            try {
                await rejected('throw');
            } catch (error) {
                console.log(error);
            }
        })()

you get a console log of the two error, with no Unhandled

@emclab
Copy link

emclab commented Feb 18, 2019

jest 24.1.0, similar deprecation warning in unit test

[Received promise resolved instead of rejected
Resolved to value: {"cell": "1234567890", "customer_id": 1, "email": "home@yahoo.com",   "  expire_datetime": "2019-02-19T19:12:51.908Z", "fort_token": "12349596874567678906", "name": "", "role": "admin"}
(node:13136) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated     either by throwing inside of an async function without a catch block, or by rejecting a promise which was   not handled with .catch(). (rejection id: 6)
(node:13136) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the   future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:13136) UnhandledPromiseRejectionWarning: Error: expect(received).rejects.toThrow()](url)

@carlohcs
Copy link

jest 24.1.0, similar deprecation warning in unit test

[Received promise resolved instead of rejected
Resolved to value: {"cell": "1234567890", "customer_id": 1, "email": "home@yahoo.com",   "  expire_datetime": "2019-02-19T19:12:51.908Z", "fort_token": "12349596874567678906", "name": "", "role": "admin"}
(node:13136) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated     either by throwing inside of an async function without a catch block, or by rejecting a promise which was   not handled with .catch(). (rejection id: 6)
(node:13136) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the   future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:13136) UnhandledPromiseRejectionWarning: Error: expect(received).rejects.toThrow()](url)

The same to version 24.4.0

@SimenB
Copy link
Member

SimenB commented Mar 12, 2019

What does your test look like? Do you return or await the expectation?

@SimenB
Copy link
Member

SimenB commented Mar 12, 2019

If your test looks like OP it's expected to still behave that way - we haven't done anything to fix it

@Codex-
Copy link

Codex- commented Apr 1, 2019

Came across this too, wasn't able to work around it so having to abandon Jest for now :(

@MickaelNeves
Copy link

There's an article on Medium covering those node warnings:

https://medium.com/dailyjs/how-to-prevent-your-node-js-process-from-crashing-5d40247b8ab2

Just made those changes on my main fetch and those warnings were gone from tests

return window.fetch(api.API_PUBLIC + endpoint + queryString, {
        method,
        headers: new Headers({
            Accept: 'application/json',
            'Content-Type': 'application/json',
            AppKey: api.APP_KEY,
            ...headers,
        }),
        ...bodyString,
    }).then(response => response.json()).then(data => {
        if (data.error) {
            return process.on('unhandledRejection', (reason, promise) => {
                promise.reject(data.error);
            });
        }

        return Promise.resolve(data);
    }).catch(error => process.on('unhandledRejection', (reason, promise) => {
        promise.reject(typeof error === 'string' ? error : error.message);
    }));

@kirillgroshkov
Copy link

any update on this?

@kawter-BST
Copy link

I have the same problem.
#Listening on port 3001...
(node:13100) UnhandledPromiseRejectionWarning: RequestError: Error: connect ECONNREFUSED 127.0.0.1:3002
at new RequestError (C:\Users\Pc\blockchain2020\programmation_BC\node_modules\request-promise-core\lib\errors.js:14:15)
at Request.plumbing.callback (C:\Users\Pc\blockchain2020\programmation_BC\node_modules\request-promise-core\lib\plumbing.js:87:29)
at Request.RP$callback [as _callback] (C:\Users\Pc\blockchain2020\programmation_BC\node_modules\request-promise-core\lib\plumbing.js:46:31)
at self.callback (C:\Users\Pc\blockchain2020\programmation_BC\node_modules\request\request.js:185:22)
at Request.emit (events.js:321:20)
at Request.onRequestError (C:\Users\Pc\blockchain2020\programmation_BC\node_modules\request\request.js:877:8)
at ClientRequest.emit (events.js:321:20)
at Socket.socketErrorListener (_http_client.js:432:9)
at Socket.emit (events.js:321:20)
at emitErrorNT (internal/streams/destroy.js:84:8)
at processTicksAndRejections (internal/process/task_queues.js:84:21)
(node:13100) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)

macbre added a commit to macbre/phantomas that referenced this issue Oct 26, 2020
…noring them

See jestjs/jest#5311 (comment)

✗ Errored » Asynchronous Error
      in Integration tests
      in test/integration-test.js
/home/macbre/github/phantomas/test/integration-test.js:19
	throw err;
	^

Error: Protocol error (Performance.getMetrics): Session closed. Most likely the page has been closed.
    at CDPSession.send (/home/macbre/github/phantomas/node_modules/puppeteer/lib/cjs/puppeteer/common/Connection.js:195:35)
    at Page.metrics (/home/macbre/github/phantomas/node_modules/puppeteer/lib/cjs/puppeteer/common/Page.js:699:45)
    at /home/macbre/github/phantomas/lib/browser.js:266:37
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
@carlpaten
Copy link

Solution found here:

Nice idea! I reworked it into a defuse function:

function defuse(promise) {
  promise.catch(() => {});
  return promise;
}

it('terminates the test prematurely', async () => {
  const promise = defuse(new Promise((resolve, reject) => setTimeout(() => reject(new Error('Oops')), 0)));
  await new Promise(resolve => setTimeout(resolve, 10));
  await expect(promise).rejects.toThrow('Oops');
});

Still, this is a workaround for a problem that I believe should be fixed.

The idea is that when you .catch() a promise you prevent it from bubbling to the top. The original promise remains rejected.

@carlosviniciusananias
Copy link

There's an article on Medium covering those node warnings:

https://medium.com/dailyjs/how-to-prevent-your-node-js-process-from-crashing-5d40247b8ab2

Just made those changes on my main fetch and those warnings were gone from tests

return window.fetch(api.API_PUBLIC + endpoint + queryString, {
        method,
        headers: new Headers({
            Accept: 'application/json',
            'Content-Type': 'application/json',
            AppKey: api.APP_KEY,
            ...headers,
        }),
        ...bodyString,
    }).then(response => response.json()).then(data => {
        if (data.error) {
            return process.on('unhandledRejection', (reason, promise) => {
                promise.reject(data.error);
            });
        }

        return Promise.resolve(data);
    }).catch(error => process.on('unhandledRejection', (reason, promise) => {
        promise.reject(typeof error === 'string' ? error : error.message);
    }));

This is a best answer!

@robertwt7
Copy link

Hey peeps, now i'm getting crashes on my test instead of warning even after wrapping my async function in try and catch. is anyone experiencing the same issue?

@github-actions
Copy link

This issue is stale because it has been open for 1 year with no activity. Remove stale label or comment or this will be closed in 30 days.

@github-actions github-actions bot added the Stale label Feb 23, 2023
@github-actions
Copy link

This issue was closed because it has been stalled for 30 days with no activity. Please open a new issue if the issue is still relevant, linking to this one.

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Mar 25, 2023
@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 26, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests