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

Axios catch error returns javascript error not server response #960

Closed
jiimmeh opened this Issue Jun 17, 2017 · 44 comments

Comments

Projects
None yet
@jiimmeh

jiimmeh commented Jun 17, 2017

Im trying to catch validation errors from the server.

Code:

axios.post('/formulas/create', {
       name: "",
       parts: ""
})
.then( 
	(response) => { console.log(response) },
	(error) => { console.log(error) }
);

Console log output

Error: Request failed with status code 422
    at createError (app.js:6765)
    at settle (app.js:29489)
    at XMLHttpRequest.handleLoad (app.js:6600)

Network tab output
{"name":["The name field is required."],"parts":["The parts field is required."]}

I should be seeing an object that contains JSON form validation as that is the response in my network tab, i seem to get the JS catch output?

Also tried the following:

axios.post('/formulas/create', {
	name: "",
	parts: ""
})
.then(response => { 
	console.log(response)
})
.catch(error => {
    console.log(error)
});

Same result

More people seem to have the same problem using the same enviroment as me here.
https://laracasts.com/discuss/channels/vue/issues-with-axios-catch-method

  • Axios version: ^0.16.2
  • VueJS 2.3.4
  • Vue-template-compiler 2.3.4
  • Laravel-mix
  • Environment: node v6.4.0, chrome 58, Mac OSX 10.12.4
@alsofronie

This comment has been minimized.

alsofronie commented Jun 18, 2017

I have exactly the same environment. Try this:

axios.post('/formulas/create', {
	name: "",
	parts: ""
})
.then(response => { 
	console.log(response)
})
.catch(error => {
    console.log(error.response)
});

Modify from console.log(error) to console.log(error.response) in catch.

You can also use a global interceptor and reject only the error.response. The problem is when the console.log tries to output the error, the string representation is printed, not the object structure, so you do not see the .response property.

@VSG24

This comment has been minimized.

VSG24 commented Aug 7, 2017

In case anybody is wondering how to reject only the response property, here's how to do it:

axios.interceptors.response.use((response) => {
    return response;
}, function (error) {
    // Do something with response error
    if (error.response.status === 401) {
        console.log('unauthorized, logging out ...');
        auth.logout();
        router.replace('/auth/login');
    }
    return Promise.reject(error.response);
});

The important part is return Promise.reject(error.response);

@gopal-g

This comment has been minimized.

gopal-g commented Sep 27, 2017

This yields the same result incase if the above solution messes up your JS file syntax while indenting or minifying :)

axios
.post('ajax/register/otp', this.registerData)
.then(function (response) {
       return otpSent(response)
})
.catch(function (error) {
      console.log(error.response);
 });
@pedro-mass

This comment has been minimized.

pedro-mass commented Oct 13, 2017

@gopal-g that's undefined for me.

@gopal-g

This comment has been minimized.

gopal-g commented Oct 18, 2017

Well @pedro-mass in that case there might be no response from server side. in my scenario my response was JSON when error occured I could get the response using error.response

@pedro-mass

This comment has been minimized.

pedro-mass commented Oct 30, 2017

@gopal-g If I'm watching the Network tab in dev tools, I can see the response. It's a 401 if that makes a difference.

@mu-yu

This comment has been minimized.

mu-yu commented Oct 31, 2017

I have the same error with @pedro-mass 's.

here is my code

async function test () {
try {
  let res = await axios.get('/xxxxx/xxxx')

} catch (e) {
  console.log(e.response) // undefined
}
}
@fabiorecife

This comment has been minimized.

fabiorecife commented Nov 5, 2017

using "error.response" does not work, for me. The problem occurs when I disconnected the database server and my web server returned error 500.

Network tab in dev tools

response code: 500
response body:

{
  "error": {
    "statusCode": 500,
    "name": "Error",
    "message": "read ETIMEDOUT",
    "code": "ETIMEDOUT",
    "errno": "ETIMEDOUT",
    "syscall": "read",
    "stack": "Error: read ETIMEDOUT\n    at exports._errnoException (util.js:1050:11)\n    at TCP.onread (net.js:582:26)"
  }
}

chrome console error:

Uncaught (in promise) Error: Request failed with status code 500
    at createError (createError.js:15)
    at settle (settle.js:18)
    at XMLHttpRequest.handleLoad (xhr.js:77)

version

"axios": "^0.15.3"

@alsofronie

This comment has been minimized.

alsofronie commented Nov 7, 2017

There is a catch in catch: if the error occurs before the request is made, then in catch you will not have the err.response property, because... well... there is no response. So, the catch will catch any error. The same applies in the situations like the one from @fabiorecife when there is no response because of a network failure. Do not treat the err as a HTTP error because this is not always the case.

@robbiemu

This comment has been minimized.

robbiemu commented Nov 17, 2017

@alsofronie so what is the preferred method of handling (distinguishing one from another) for example preflight failures like 413?

@paolavness

This comment has been minimized.

paolavness commented Nov 29, 2017

I'm getting a similar issue - Chrome devtools is showing a 401, the response is {"message":"Identity token has expired"} and I am catch( (error) => {...}) - but error.response blank. I am wondering if this is because the preceding OPTIONS call associated with the GET has a status of 200. What to do in this case and where is the right place to retrieve the 401 status code?

@mrchief

This comment has been minimized.

mrchief commented Nov 29, 2017

@robbiemu I think its already documented: https://github.com/axios/axios#handling-errors

@paolavness

This comment has been minimized.

paolavness commented Nov 29, 2017

@mrchief thanks for this, good to go over it again. In my case, this solution has works in most cases but the current problem I am experiencing is that the error.response object is present, but fields are undefined, any calls to values inside the response object result in an error.

@mrchief

This comment has been minimized.

mrchief commented Nov 29, 2017

@paolavness I'd suggest to open up a new issue instead of commenting here. Not many watch closed issues. Or maybe even ask it on StackOverflow.

@paolavness

This comment has been minimized.

paolavness commented Nov 29, 2017

@mrchief Ah this is closed, thanks for pointing that out. will do.

@dbshoupe

This comment has been minimized.

dbshoupe commented Dec 27, 2017

I had the same issue. Ultimately, I changed the error code from 401 to 403 and everything worked as I expected it to. I'm guessing the reason for the empty error object and resulting javascript error has to do with this statement that I found in the documentation for 401 errors:

"[A 401 error] response must include a WWW-Authenticate header field containing a challenge applicable to the requested resource."

So if you are going to utilize a 401 error, you must include a www-authenticate header field. If you don't want to do this, just use a 403 error.

@juliano-barros

This comment has been minimized.

juliano-barros commented Dec 27, 2017

Good morning guys, I had this issue, however, I fixed it changing my apache configuration to enable cors on my server. Have a look at link below

https://enable-cors.org/server_apache.html

@richellyitalo

This comment has been minimized.

richellyitalo commented Mar 17, 2018

You can debug all response only with console.log(JSON.stringify(error))

@maythukha

This comment has been minimized.

maythukha commented Apr 29, 2018

axios.post(url)
.then((response) => {
// something
})
.catch((response) => {
if (response.status == undefined) {
alert('unauthorized')
}
})

@petruscurtu

This comment has been minimized.

petruscurtu commented May 22, 2018

I had exactly the same problem described by @fabiorecife . I solved it but it is not the most elegant solution and I do not understand why this is needed.

.catch(error=>{
let errorObject=JSON.parse(JSON.stringify(error));
console.log(errorObject);
dispatch(authError(errorObject.response.data.error));
})

This produces the expected results in the errorObject variable.

@luxueyan

This comment has been minimized.

luxueyan commented May 26, 2018

@petruscurtu I have the same problem! It dose not work for me!
image
I don't khnow why it throw 'no Access-control-allow-origin', but the server has set it! The response header is ok
image

@petruscurtu

This comment has been minimized.

petruscurtu commented May 26, 2018

@luxueyan I do not know what the problem could be since I do not know how you handle CORS on your server. As far as axios is concerned, I can see a config and request object in your console output. I think that you should check that output. It might be related to the format of the response your server is sending back.

@chiefie

This comment has been minimized.

chiefie commented May 28, 2018

@luxueyan It seems that you had your Access-control-allow-origin set up for http://localhost:8081 (assuming this is set on the server with this address http://10.31.143.32:8080?) Can you check what is the URL you're running this project from? Is it http://localhost:8081? If it is not, even if the port number is different, then it is violating the CORS domain rules.

@luxueyan

This comment has been minimized.

luxueyan commented May 29, 2018

@petruscurtu The output is 'JSON.parse(JSON.stringify(error))' in interceptor of response. There has not response property

@luxueyan

This comment has been minimized.

luxueyan commented May 29, 2018

@chiefie I am sure that 'Access-control-allow-origin' setting is ok since other ajax api can response right content!Only the capcha code api go wrong。 when it is expired, it response error with 478 status and axios can not get the response body!

Update! It was the server api error, it didn't have cors settings in header. Now work well.

@the94air

This comment has been minimized.

the94air commented Jun 9, 2018

Thanks @juliano-barros it was because of the cross-origin. Now all my ajax requests works just fine.

@leodutra

This comment has been minimized.

leodutra commented Jun 13, 2018

This raw error is thrown whenever server is unreacheable/CORS invalidated.
error.response will just be undefined.

I just created an IntegrationError which contains message, status and response data... but always following the same structure. This way I can decouple axios/raw error from my handlings.

@leodutra

This comment has been minimized.

leodutra commented Jun 13, 2018

Axios guys, this should be a HTTP error I think... including an error.response.
This can be checked as above

@frostshoxx

This comment has been minimized.

frostshoxx commented Jun 18, 2018

Yesterday was the one-year anniversary of this issue. Will it ever get "fixed" or is it considered to be a "non-issue" at this point?

@jacurtis

This comment has been minimized.

jacurtis commented Jun 19, 2018

@frostshoxx So i think this is a "non-issue". While I am experiencing the same problem as everyone else, i now understand what is happening and I can see that this isn't a bug or issue, but just the way the world works.

For everyone that is confused (and in celebration of this issue turning one year old) let me explain what is happening and how to get your server response.

How Did you End Up Here?

There are many reasons you might encounter the .catch() wasteland. Sometimes Axios encounters an error early on in the request. This sometimes happens with CORS requests. When Axios OPTIONS isn't available, than Axios panics and sends you to .catch() before it actually gets a response from the server, so the response isn't available in these cases.

Some of you might say, "No, i see it in DevTools, so the response came back!". This isn't always true because sometimes Axios will kick off an HTTP request, experience an error, abort itself and send you to .catch() before the server responded back. A few milliseconds after Axios runs the .catch() function than your server might come back with a response, which shows up in DevTools because it is still listening for the response. However, Axios is long done at this point, so the response wasn't available because it didn't exist at the time that Axios initiated the .catch() function.

Those of you that get a message about a 4XX or 5XX error from the server, are in luck! There is usually a response from the server available to you because in order to get this error, Axios had to wait around for the full server response. In these cases, Axios sends you to .catch() with the response but it wraps the response in its own error object that it creates on the fly.
So if you are getting 5XX or 4XX errors, than you probably have the server response available to you, but it is buried DEEP!

Try error.response.data.data.message. This is confirmed to work for Laravel API requests that return server errors.

Non-Laravel users -- You will at least need to go error.response.data to get to the core response from the server, but most servers than wrap their responses in another data{} object requiring you to go a layer deeper, and then finally access the response property you want like .message from there.

But I Did Console.Log and It Contains No Response!

Yeah i know, but just try to output error.response.data anyway just to humor me.

For some reason Axios modifies the output of console.log(error) on the error object. I am not sure why they do this, but if you instead write console.log(JSON.stringify(error)) than you will see the whole object in all its glory.

This is serialized JSON which gets hard to read after about 2 layers deep. So you should copy the full thing and paste it into a JSON prettifyer to explore the error object's structure.


If you are getting a 500 error or a 400 type error (or really any server supplied error code) in Axios, then that means that the request completed and that the server's response is available to you!

The problem is that it's super deep, and because Axios modifies the output of our console.log(error) that most people do to see the error{} object, most people don't know that it exists. But it's there!

@empitechathu

This comment has been minimized.

empitechathu commented Jun 19, 2018

@VSG24 man!! that's a life-saving answer :D

@hhowe29

This comment has been minimized.

hhowe29 commented Aug 23, 2018

fwiw, JSON.stringify(error) bombs in 0.18.0 due to circular references.

@alimirayman

This comment has been minimized.

alimirayman commented Sep 4, 2018

I set up an interceptor which gives me server response on error

axios.interceptors.response.use(
  response => {
    // do someting on response
    return response
  },
  error => {
    // do someting on errir
    return Promise.reject(error.response.data) // => gives me the server resonse
  }
)
    try {
      const { data } = await htttp.post(login, payload)
      // do someting with data
    } catch (e) {
      console.log(e) // server response data
    }
@AllanPinheiroDeLima

This comment has been minimized.

AllanPinheiroDeLima commented Sep 10, 2018

@alimirayman It seems to work fine if we don't have to keep an eye on every request. But, when you have to track every request, it's not reasonable to have an error handler repeated in every location in my code. Unless I understood wrong ( wich may be the case ), axios should'nt have to make sure the server has returned an error before it throws an error ?

@davalapar

This comment has been minimized.

davalapar commented Sep 13, 2018

@hhowe29 use circular-json

@stemid

This comment has been minimized.

stemid commented Sep 17, 2018

I found this thread like many others and I'm using axios with vue.js and vuex modules.

My vuex module action catches the axios error and runs commit(ERROR_HAPPENED, error) to run the ERROR_HAPPENED mutation of the vuex module. But the error arrives without its response property resulting in error.response being undefined if I attempt to set the response to a vuex state.

If I console.log(error.response) in the action, before the commit(), I can see the response. So it's being mangled by vuex most likely.

Either commit(ERROR_HAPPENED, error.response.data) or check if it's a 401 in the action and run some other commit depending on the status code. Because this mangling only happens with a 401 code. Other errors like 404 or 500 are passed through to my mutations.

@leodutra

This comment has been minimized.

leodutra commented Sep 26, 2018

Not mangled. Vuex does not mangle this, not even by bug.
Some kinds of errors, mostly inside Axios interceptors, cause this to not be exposed.

@Sphinxs

This comment has been minimized.

Sphinxs commented Sep 29, 2018

Some update ?

@alimirayman

This comment has been minimized.

alimirayman commented Oct 2, 2018

@AllanPinheiroDeLima sorry for the late reply but I don't think anyone writes intercepter on each and every single location of anyone's code. We write intercepter one time on a code base and use it as a helper class, that's number 1. Number 2, the initial query was they did not get server error response, instead, they got the javascript error in the try catch block. there were 2 solution one to change the way server responded error or the way frontend catched error. axios error intercepter is basically acts as a middleware to solve that issue if you don't want to change your ways to code or change a large code base.

@AllanPinheiroDeLima

This comment has been minimized.

AllanPinheiroDeLima commented Oct 3, 2018

Yeah, I agree. I found a solution to this problem making the integration into my own server instead using raw keycloak. This way I could intercept the error in the server and then send a legible one to the front end. I think that’s the only way to solve this kind of issue for now :)

@emersoncpaz

This comment has been minimized.

emersoncpaz commented Oct 14, 2018

I'm having the same problem yet. Using in Axios interceptor console.log('interceptors', JSON.stringify(error)) i get this log interceptors {"config":{"transformRequest":{},"transformResponse":{},"timeout":0,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","maxContentLength":-1,"headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json;charset=utf-8"},"method":"post","url":"http://localhost:5000/api/autenticacao/login","data":"{\"siglaInstituicao\":\"NEAS\",\"usuario\":\"emersoncpaz\",\"senha\":\"belle1903\"}"},"request":{}} but chrome devtools Response i have the correct response from the api : A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server) How to get this response from axios?

@jonasgroendahl

This comment has been minimized.

jonasgroendahl commented Oct 19, 2018

Any reason 5xx errors are not handled by catch block (doesn't return a response object)? Status code 502 appear in DevTool console but doesn't get handled by Axios.

@felipanico

This comment has been minimized.

felipanico commented Nov 7, 2018

This worked for me (with laravel abort helper: abort(500, 'message'))

.catch(error => {
   console.log(error.response.data.message);
});

@movepixels

This comment has been minimized.

movepixels commented Nov 12, 2018

Does anyone one have a full working example other than snip of code saying this worked? Snip of 1 line does not help much if you have no idea who they were referring to in this thread, where they put that snip of code, what the final working code looks like?

I am trying to catch the error in the inerceptor and based on the error code either end the request there with redirect if 401 to login, or if its 422 let the request go back to the calling component to allow updating of the form errors.

Any ideas?

I am using Nuxt axios and my onResponseError interceptor looks like:

$axios.onResponseError(error => {
    const code = parseInt(error.response && error.response.status)
    console.log(code, 'Axios onResponseError')
    console.log(error.response.status, 'status')
    console.log(error.config, 'config')
    console.log(error.response, 'response')
    // Do something with response error
    if (error.response.status === 401) {
      console.log('unauthorized, logging out ...')
      app.$auth.logout()
      redirect('/login')
    }
    if (code === 400) {
      // redirect('/400')
      console.log(`400 onResponseError`)
    }
    if (code === 408) {
      console.log(`A timeout happened on url onResponseError`)
    }
    if (code === 418) {
      console.log(`A teapot onResponseError`)
    }
    if (code === 428) {
      console.log(`428 onResponseError`)
      // redirect('/login')
    }
    return Promise.reject(error)
  })

And a sample call would look like:

async postRecord (clear = false) {
      try {
        const { r } = await this.$axios.$post(this.endPoint, this.formData)
        console.log(r, 'response')
        // do something with response
      } catch (e) {
        console.log(e, 'error in crud') // server response data
        // i should never get here if error since the interceptor should kill the request if 401
       // for testing this request is returning a 401
      } finally {
        this.submitted = false
      }
    },

Thanks,
Dave

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