No stacktrace on NavigationDuplicated error uncaught in promise
See original GitHub issueVersion
3.1.1
Reproduction link
https://jsfiddle.net/Lom7hxn5/
Steps to reproduce
- Call
$router.replace
or$router.push
twice with same path
What is expected?
Error object provides stacktrace (stack property)
What is actually happening?
No stack trace in the error object
When NavigationDuplicated
error is thrown, there is no stack trace in the error itself so when error is reported through error reporting service like Sentry
, there is absolutely no context to go with the error and so it’s hard to figure out where that error originated.
Transpiled code for the NavigationDuplicated
class looks like this:
https://github.com/vuejs/vue-router/blob/5141def3a21da479c2858c0d70db3cb438c3b7d0/dist/vue-router.common.js#L1935-L1946
which is problematic because instantiating such function won’t create a stacktrace. It would work fine with non-transpiled code (although I’m not suggesting to not transpile it): https://github.com/vuejs/vue-router/blob/44c63a963f05ede229d8658a45b2388e244ce00b/src/history/errors.js#L1
One possible solution would be to manually set stack trace with:
this.stack = (new Error()).stack;
but I’m not sure how that would be done if transpilation is done by webpack/babel…
There is also one extra bug in that code. The line that instantiates error passes a route object to the error: https://github.com/vuejs/vue-router/blob/fc42d9cf8e1d381b885c9af37003198dce6f1161/src/history/base.js#L125 but error doesn’t do anything with it: https://github.com/vuejs/vue-router/blob/44c63a963f05ede229d8658a45b2388e244ce00b/src/history/errors.js#L2
Saving route properties on the error object would also help in deciphering source of the error.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:25
- Comments:35 (15 by maintainers)
I understand it may be a bit confusing to see the UncaughRejection error in the console, so let me summarize what it’s all about:
The error you see in the console is part of the new promise api: before, if no callbacks were supplied to
router.push
, errors were only sent to the global router error handler. Now, because bothpush
andreplace
return a promise, if the navigation failure (anything that cancels a navigation like anext(false)
ornext('/other')
also counts) is not caught, you will see an error in the console because that promise rejection is not caught. However, the failure was always there because trying to navigate to same location as the current one fails. It’s now visible because of the promise being rejected but not caught.This behavior doesn’t happen with
router-link
: no errors are emitted (except globally). This is because it’s catching the error by adding a noop callback, so you don’t need to catch anything when usingrouter-link
. You can catch the rejection when doing programmatic navigation and ignore it:But if you are using
router.push
in your code and you don’t care about the navigation failing, you should catch it by usingcatch
:The last version makes more sense as the Promise api is likely to become the default and the callback version to become deprecated.
This isn’t a breaking change because the code still works the same it was before. The only exceptions are if you were doing one of these:
However,
router.push
never returned anything before 3.1, so both cases were invalid usage. Even worse, if you were waiting for the resolution ofrouter.push
, it never worked because the function wasn’t returning a promise, so the navigation wasn’t even resolved nor failed after callingpush
. Now that it returns a Promise, it is possibly exposing bugs that were not visible before.If you want to handle this globally, you can replace Router’s push/replace functions to silence the rejection and make the promise resolve with the error instead:
I discourage from swallowing the errors like this because doing
await router.push()
will always resolve even when the navigation fails.With the new navigation failures, you can replicate vue-router-next behavior:
This was reported already at https://github.com/vuejs/vue-router/issues/2872 and https://github.com/vuejs/vue-router/issues/2873. This issue is about improving the stack trace of the Rejection so if you could keep the conversation about that, it would help me a lot! I hope that this answer, being more detailed brings clarity to the topic. I’ve also been looking at how this impacts Nuxt with their core team so the error doesn’t appear if it shouldn’t.
hey @posva thanks so much for the thorough breakdown. I think the confusion is coming into play regarding what you described when you said:
“However, the failure was always there because trying to navigate to same location as the current one fails. It’s now visible because of the promise being rejected but not caught.”
While I understand that the above is true, I don’t think it was unreasonable for anyone to assume that the router’s default behavior would be to handle the NavigationDuplicated usecase “out of the box”… I’ve been using vue-router for several years, and I thought this was just default functionality all along.
I’m absolutely a fan of the promise API and see some immediate benefits, but I’ve had to rollback vue-router to a pre 3.1 version because I don’t have a way of cleaning up the errors that are being thrown without adding
catch
to all of myrouter.push
’s.Perhaps we can spin up a separate “feature request” issue for handling NavigationDuplicated out of the box?