Angular 1.6.0: "Possibly unhandled rejection" error [duplicate]
Asked Answered
I

11

77

We have a pattern for resolving promises in our Angular app that has served us well up until Angular 1.6.0:

    resource.get().$promise
        .then(function (response) {
        // do something with the response
        }, function (error) {
            // pass the error the the error service
            return errorService.handleError(error);
        });

And here is how we are triggering the error in Karma:

    resourceMock.get = function () {
        var deferred = $q.defer();
        deferred.reject(error);
        return { $promise: deferred.promise };
    };

Now, with the update to 1.6.0, Angular is suddenly complaining in our unit tests (in Karma) for rejected promises with a "Possibly unhandled rejection" error. But we are handling the rejection in the second function that calls our error service.

What exactly is Angular looking for here? How does it want us to "handle" the rejection?

Inandin answered 9/12, 2016 at 15:38 Comment(3)
I noticed this in our code base as well. Weirdly, running the suite with the chrome launcher works fine. PhantomJS is the one complaining.Remarkable
If you get this and didn't just upgrade, here's how you can check your Angular version: #16018199Octavie
The accepted answer recommends hiding the error.The duplicate suggests a more robust alternative with this answer.Macroscopic
B
67

Try adding this code to your config. I had a similar issue once, and this workaround did the trick.

app.config(['$qProvider', function ($qProvider) {
    $qProvider.errorOnUnhandledRejections(false);
}]);
Benzine answered 2/2, 2017 at 2:15 Comment(3)
I must point out that this merely hides the error, according to developers there is some issue in code,and it just masks the error.Strudel
Agree, while it may shun the issue the root cause remains and the developer should handle error case instead.Duration
What config file specifically?Bacterium
G
25

The code you show will handle a rejection that occurs before the call to .then. In such situation, the 2nd callback you pass to .then will be called, and the rejection will be handled.

However, when the promise on which you call .then is successful, it calls the 1st callback. If this callback throws an exception or returns a rejected promise, this resulting rejection will not be handled, because the 2nd callback does not handle rejections in cause by the 1st. This is just how promise implementations compliant with the Promises/A+ specification work, and Angular promises are compliant.

You can illustrate this with the following code:

function handle(p) {
    p.then(
        () => {
            // This is never caught.
            throw new Error("bar");
        },
        (err) => {
            console.log("rejected with", err);
        });
}

handle(Promise.resolve(1));
// We do catch this rejection.
handle(Promise.reject(new Error("foo")));

If you run it in Node, which also conforms to Promises/A+, you get:

rejected with Error: foo
    at Object.<anonymous> (/tmp/t10/test.js:12:23)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.runMain (module.js:604:10)
    at run (bootstrap_node.js:394:7)
    at startup (bootstrap_node.js:149:9)
    at bootstrap_node.js:509:3
(node:17426) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: bar
Gawen answered 9/12, 2016 at 16:31 Comment(5)
Good points! Yes, the intent is to handle rejection that occurs before the call to .then. The code that is triggering this rejection (and which worked for Angular 1.5.9) has been added to the original question.Inandin
How to actually fix asked pattern code?Lake
Ok, fix is like $promise.then(success).catch(error), where catch catches all errors, more in "Note" section at migrationLake
this really should be the correct answerKesha
What if your function returns a rejected promise by design? Similar to the way $http.get() returns a promise that by design is rejected if the request fails. I have functions which return promises, and in certain cases they can return $q.reject(err), it seems that angular doesn't like this unless I clobber the warnings?Vasos
D
22

The first option is simply to hide an error with disabling it by configuring errorOnUnhandledRejections in $qProvider configuration as suggested Cengkuru Michael

BUT this will only switch off logging. The error itself will remain

The better solution in this case will be - handling a rejection with .catch(fn) method:

resource.get().$promise
    .then(function (response) {})
    .catch(function (err) {});

LINKS:

Dixson answered 28/7, 2017 at 8:12 Comment(0)
I
19

Found the issue by rolling back to Angular 1.5.9 and rerunning the test. It was a simple injection issue but Angular 1.6.0 superseded this by throwing the "Possibly Unhandled Rejection" error instead, obfuscating the actual error.

Inandin answered 12/12, 2016 at 16:8 Comment(2)
This is a big problem for me currently as I'm migrating a code base and replacing an old injected dependency. Did you find a way to prevent that obfuscation and get it to throw the real error?Mercerize
The top answer above will fix it in running code, but I couldn't get it to throw the error in Karma without rolling back to Angular 1.5.9.Inandin
S
8

To avoid having to type additional .catch(function () {}) in your code in multiple places, you could add a decorator to the $exceptionHandler.

This is a more verbose option than the others but you only have to make the change in one place.

angular
    .module('app')
    .config(configDecorators);

configDecorators.$inject = ["$provide"];
function configDecorators($provide) {

    $provide.decorator("$exceptionHandler", exceptionHandler);

    exceptionHandler.$inject = ['$delegate', '$injector'];
    function exceptionHandler($delegate, $injector) {
        return function (exception, cause) {

            if ((exception.toString().toLowerCase()).includes("Possibly unhandled rejection".toLowerCase())) {
                console.log(exception); /* optional to log the "Possibly unhandled rejection" */
                return;
            }
            $delegate(exception, cause);
        };
    }
};
Suomi answered 27/5, 2018 at 19:10 Comment(0)
L
5

You could mask the problem by turning off errorOnUnhandledRejections, but the error says you're needing to "handle a possible rejection" so you just need to add a catch to your promise.

resource.get().$promise
    .then(function (response) {
    // do something with the response
    }).catch(function (error)) {
        // pass the error to the error service
        return errorService.handleError(error);
    });

Reference: https://github.com/angular-ui/ui-router/issues/2889

Ligature answered 16/5, 2017 at 18:32 Comment(0)
S
3

Please check the answer here:

Possibly unhandled rejection in Angular 1.6

This has been fixed with 316f60f and the fix is included in the v1.6.1 release.

Scotland answered 23/12, 2016 at 4:20 Comment(3)
Still getting this in 1.6.2Adermin
While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From ReviewDigraph
@MostafaTorbjørnBerg thanks. I updated the answer.Scotland
H
2

I have observed the same behavior during test execution. It is strange that on production code works fine and fails only on tests.

Easy solution to make your tests happy is to add catch(angular.noop) to your promise mock. In case of example above it should looks like this:

resourceMock.get = function () {
    var deferred = $q.defer();
    deferred.reject(error);
    return { $promise: deferred.promise.catch(angular.noop) };
};
Hauberk answered 15/12, 2016 at 10:6 Comment(0)
J
2

I was also facing the same issue after updating to Angular 1.6.7 but when I looked into the code, error was thrown for $interval.cancel(interval); for my case

My issue got resolved once I updated angular-mocks to latest version(1.7.0).

Jurel answered 4/6, 2018 at 16:21 Comment(0)
C
1

I had this same notice appear after making some changes. It turned out to be because I had changed between a single $http request to multiple requests using angularjs $q service.

I hadn't wrapped them in an array. e.g.

$q.all(request1, request2).then(...) 

rather than

$q.all([request1, request2]).then(...)

I hope this might save somebody some time.

Cangue answered 26/6, 2018 at 14:16 Comment(0)
S
0

It may not be your speficic situation, but I had a similar problem.

In my case, I was using angular-i18n, and getting the locale dictionary asynchronously. The problem was that the json file it was getting was incorrectly indented (mixing spaces and tabs). The GET request didn't failed.

Correcting the indentation solved the problem.

Stokehold answered 29/1, 2018 at 13:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.