mstdmstd
mstdmstd

Reputation: 3115

Got Uncaught (in promise) NavigationDuplicated error on invalid credentials

In my Laravel 5.8 / "vue": "^2.6.10"/ "vuex": "^3.1.0" app In resources/js/components/Login.vue file I have method

methods: {
    authenticate() {
        this.$store.dispatch('login');    // calling action

        login(this.$data.form)
            .then((res) => {

                this.$store.commit("setLoginSuccess", res);  // calling mutation
                this.$store.dispatch('retrieveHostelBookmarks', res.user.id);
                this.$store.dispatch('retrievePersonalOptions', res.user.id);


                this.$router.push({path: '/personal'}); // For debugging!
            })
            .catch((error) => {
                console.log("=== error::")
                console.log(error)
                this.$store.commit("setLoginFailed", {error});   // calling mutation
            });
    }

and in resources/js/helpers/authFuncs.js I have definition :

export function login(credentials) {
    return new Promise((res, rej) => {
        axios.post('/api/auth/login', credentials)
            .then((response) => {
                setAuthorizationToken(response.data.access_token);
                res(response.data);
            })
            .catch((err) =>{
                console.error(err)
                rej("Wrong email or password");
            })
    })
}

and the problem is that on invalid credentials in the console I see promise warning in the end of output :

VM836:1 POST http://127.0.0.1:8084/api/auth/login 401 (Unauthorized)
(anonymous) @ VM836:1
dispatchXhrRequest @ app.js?dt=1571914585:311
xhrAdapter @ app.js?dt=1571914585:150
dispatchRequest @ app.js?dt=1571914585:758
Promise.then (async)
request @ app.js?dt=1571914585:560
Axios.<computed> @ app.js?dt=1571914585:585
...
app.js?dt=1571914585:10042 === error::
app.js?dt=1571914585:10043 Wrong email or password
app.js?dt=1571914585:131483 Uncaught (in promise) NavigationDuplicated {_name: "NavigationDuplicated", name: "NavigationDuplicated", message: "Navigating to current location ("/login") is not allowed", stack: "Error↵    at new NavigationDuplicated (http://127.…/127.0.0.1:8084/js/app.js?dt=1571914585:148080:12"}

Why this warning and how to fix it?

Upvotes: 5

Views: 10835

Answers (6)

Nikola
Nikola

Reputation: 11

easiest solution

this.$router.push('/something').catch(err => { return err })

Upvotes: 1

agm1984
agm1984

Reputation: 17170

I just had this problem in a Vue JS SPA app due to it requesting GET /api/user immediately after a page load that would cause an error 401 expired JWT token.

I can't see which order those occured, but I can see the middleware ran first, and then the axios.interceptors.response.use(response => response, (error) => {}) ran second.

Both were calling this.$route.push({ name: 'login' }). While the interceptor was running, the route-change was already in progress.

My solution was to retain the middleware's this.$route.push({ name: 'login' }), and then I changed the interceptor to:

import router from '~/router';

...

if (router.currentRoute.name !== 'login') {
    router.push({ name: 'login' }).catch(() => {});
}

I chose to match it based on the route name so that I wasn't hard-coding /login. It effectively skips the would-be duplicate route-change if the login route is already loading when the interceptor checks what it should do as a result of the expired token.

Upvotes: 2

We can use this.$router.push(route, () => {}); to get rid of the error.

Upvotes: 3

mstdmstd
mstdmstd

Reputation: 3115

Looks like decision was simple, as error.response has ref to the current page:

if ( error.response.config.url != '/api/auth/login' ) {
    router.push('/login');
}

and that works for me...

Upvotes: 0

muya.dev
muya.dev

Reputation: 975

You can not have a router-link pointing to the current link. See this Github issue

I would suggest that you check if the router-link points to the current link before redirecting, see below code

if (this.$router.path !== '/login') {
    this.$router.push('/login')
}

You need to redirect if the router path is not /login. I hope my answer helps you.

Upvotes: 1

mstdmstd
mstdmstd

Reputation: 3115

MODIFIED # 2 : I found WHERE error is triggered! In my resources/js/app.js I have :

const router = new VueRouter({
    routes,
    mode: 'history'
});

checkAuthorization(store, router);
axios.interceptors.response.use(null, (error) => {

    if (error.response.status == 401) {

        store.commit('setLogout');

        console.log("0012 route.path::")
        console.log( $route )      // THAT OUTPUTS NOTHING
        console.log( $route.path ) // THAT OUTPUTS NOTHING

        router.push('/login');  // THAT RISE ERROR !
    }

    return Promise.reject(error);
});


const app = new Vue({
    el: '#app',
    router,
    store,
    bus,
    components: {
        mainapp, appheader, appfooter, navigation
    },
});


router.afterEach(( to, from ) => {
    bus.$emit('page_changed', from, to);
});

and line

router.push('/login');

raise this error, but I do not know how can I can I check current path

I the provided link I read :

Note: $route is an object provided by vue-router to every component

But $route is empty. I think I need to get $route.path inside of

axios.interceptors.response.use

How ?

MODIFIED # 3 : I tried to wrap in try block

try {
    router.push('/login');
}
catch(error) {
    console.log("0014 route.path::")
}

but got error anyway... Any decision ?

Upvotes: 0

Related Questions