Axios error status code UnhandledPromiseRejectionWarning - javascript

Using axios, I'm making GET requests to multiple links to test to see if they are broken links. If the GET requests returns an error, I want to log this error to the console and send it to the client side with socket. However, on some links (not all), I get an UnhandledPromiseRejectionWarning.
// check if the link is dead
axios.get(linkHref, {
auth: {
username: USERNAME,
password: PASSWORD
}
})
.then( (response) => {
if (response.status != 200) {
resultData = {
text: linkText,
url: linkHref,
statusCode: String(response.status)
}
console.log(resultData);
io.sockets.emit("result", resultData); // send link to client-side
}
})
.catch( (error) => {
if (error) {
resultData = {
text: linkText,
url: linkHref,
statusCode: String(error.response.status) // this is where the error is
}
console.log(resultData);
io.sockets.emit("result", resultData); // send link to client-side
}
});
I expect it work correctly and return the status code of the error but sometimes I get a UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'status' of undefined error. But with my if statement in the .catch, I am checking to make sure that there is an error and that it is not undefined so I don't understand why I'm getting this error.
EDIT: Also, this works (does not show me that error) when I run it on my Mac but not in Windows ... why?

TypeError: Cannot read property 'status' of undefined The response property on error.response is not defined eg error = { response: undefined }. Therefore, referencing any properties on it throws an error. error.response[property]
Also on failed/hanging http requests the error argument is different. Checkout this gist axios-catch-error. The error argument will have different properties for this case.

hey can you check by validating whether you are getting the response property on the error object in the if else condition
.catch( (error) => {
if (error.response) {
//rest of the code...
}
});

Related

How to use interceptors to handle response globally

I have just started exploring axios and using interceptors
In my code, I have axios api call here and there, So I used interceptors to handle error,
but it still show uncaught error in my console. I dont know why is that?
For example, this is my api code:
export const getcountryAllocations = async (country: string) => {
const response = await instance.get<IcountryAllocationTypeResponse[]>(
`/asa/${country}`
)
return response.data
}
So, in order to handle error in a centralized way, I used interceptors to handle error.
export const instance = axios.create()
instance.interceptors.response.use(
res => {
return res
},
error => {
if (error.response.status === 401) {
console.log(error)
}
throw error
}
)
But I still get a red error in console when I get api request error, say the example api I mentioned above
GET http://localhost:30087/asa/USD 401 (Unauthorized)
AxiosError {message: 'Request failed with status code 401', name: 'AxiosError', code: 'ERR_BAD_REQUEST', config: {…}, request: XMLHttpRequest, …}
localhost/:1 Uncaught (in promise) AxiosError {message: 'Request failed with status code 401', name: 'AxiosError', code: 'ERR_BAD_REQUEST', config: {…}, request: XMLHttpRequest, …}
But I expect that it shouldnt display localhost/:1 Uncaught anymore, as I have handled it in interceptors
You are catching the error but then throwing it again. That's why you still have the red error in the console.
What you can do is catch the error and then return an object saying an error occurred without throwing it again.
export const instance = axios.create()
instance.interceptors.response.use(
res => {
return res
},
error => {
if (error.response.status === 401) {
console.log(error)
}
return {
success: false,
message: error.message,
status: error.response.status
}
}
)

Testing 400 status code axios throws error

I'm creating an api test framework for a project I am working on and I'm looking to validate required fields in JSON objects being sent to an endpoint.
I'm trying to send a JSON object with a missing field and expecting a 400 response from my application with a validation message. But when making this call with axios it (somewhat rightly) throws an error as it received the 400.
I want to be able to assert that 400 is expected and to assert on the validation message.
All the examples I've come across are all regarding dealing with the 400 response in the correct way you would if you are not expecting the 400 response, but i am expecting it.
I couldn't seem to find anyone else trying this.
async function createReminder(reminderObject) {
const response = await axios.post(`${config.get('baseUrl')}/reminder`, {
...reminderObject
});
return response;
}
module.exports.createReminder = createReminder;
Here is my working code as it stands. This will make a valid 200 when being used with a valid call.
I need to have the 400 response / validation message be returned in teh response object to be handled in the function that calls this function.
In effect, you want to intercept the response and transform it as you wish. There's a section in the axios docs dedicated to interceptors. As an example of what you can do with it, here is what I've used in a recent project with a Laravel back-end:
axios.interceptors.response.use(
res => Promise.resolve(res.data),
error => {
let message = null;
if (error.response) {
// if error has a data.error property, it's an error formatted by server
if (error.response.data.error) message = error.response.data.error;
else if (error.response.status === 500) message = 'Oops! Something went wrong.';
} else {
// if error has a code property, it's an error defined by axios
if (error.code === 'ECONNABORTED') message = 'Timeout exceeded';
else message = 'Oops! Something went wrong.';
}
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject({ error: message });
}
);
The code above allows me to make axios calls as follows, and always expect the same format:
axios.post('/url', { data: { ... }})
.then(({ jsonProp1 }) => console.log(jsonProp1))
.catch(({ error }) => console.log(error);
Note: if you do create a new axios instance, you need to reinject the response interceptor for this instance too.

Angular Error TypeError: You provided an invalid object where a stream was expected

So something strange is going on, I am making an HTTP get in my service but I am getting an error, the strange thing is it sometimes works and sometimes doesn't, but for the past 24 hours it's not been working anymore. Code for a service call and subscription are below. and then also the error.
category.service.ts
getcategory(id) {
const url = `${this.localUrl}category/${id}`;
return this.http.get<CategoryResponse>(url)
.pipe( catchError(this.errorHandler) );
}
Error Handler
errorHandler(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else {
// The backend returned an unsuccessful response code.
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
}
// return an observable with a user-facing error message
return throwError('Something bad happened; please try again later.');
}
add-edit-category.component.ts
if (this.router.url.indexOf('edit') >= 0) {
this.route.paramMap.pipe(switchMap((params: ParamMap) =>
this.categoryService.getcategory(params.get('id'))))
.subscribe(results => {
this.category = results.category;
this.categoryNameControl.setValue(this.category.categoryName);
this.categoryDescriptionControl.setValue(this.category.categoryDescription);
console.log("I am edit", Object.keys(config))
console.log("TEst again", this.category)
}, error => {
console.log("Error", error)
});
}
Error
Error TypeError: You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
at Object.push../node_modules/rxjs/internal/util/subscribeTo.js.exports.subscribeTo (subscribeTo.js:42)
at Object.subscribeToResult (subscribeToResult.js:7)
at SwitchMapSubscriber.push../node_modules/rxjs/internal/operators/switchMap.js.SwitchMapSubscriber._innerSub (switchMap.js:103)
at SwitchMapSubscriber.push../node_modules/rxjs/internal/operators/switchMap.js.SwitchMapSubscriber._next (switchMap.js:96)
at SwitchMapSubscriber.push../node_modules/rxjs/internal/Subscriber.js.Subscriber.next (Subscriber.js:103)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/map.js.MapSubscriber._next (map.js:81)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:93)
at BehaviorSubject.push../node_modules/rxjs/_esm5/internal/BehaviorSubject.js.BehaviorSubject._subscribe (BehaviorSubject.js:26)
at BehaviorSubject.push../node_modules/rxjs/_esm5/internal/Observable.js.Observable._trySubscribe (Observable.js:176)
at BehaviorSubject.push../node_modules/rxjs/_esm5/internal/Subject.js.Subject._trySubscribe (Subject.js:96)

How to handle empty json responses from server

I have a function in my front end app which calls my node.js backend server:
Client function:
this.geocode = (placeName) => {
const url = '/api/twitter/geocode?' + 'query=' + encodeURIComponent(placeName);
return fetch(url)
.then(processResponse)
.catch(handleError)
}
// API Helper methods
const processResponse = function (response) {
if (response.ok) {
console.log(response);
return response.json()
}
throw response;
}
const handleError = function (error) {
if (error.json) {
error.json().then(error => {
console.error('API Error:', error.message || error)
})
} else {
console.error('API Error:', error.message || error)
}
}
Server route:
app.get('/api/twitter/geocode', (req, res) => {
var parameters = {
query: req.query.query
}
Twitter.get('geo/search', parameters)
.then(response => {
console.log("RESPONSE:")
console.log(response);
// check if there is a place for the given query
if(response.date.places.length > 0){
res.send(response.data.result.places[0].bounding_box.coordinates[0][0]);
}
res.send()
})
.catch(e => res.status(500).send('Something broke!')
)
});
There is a problem when placeName is the name of a place that doesn't exist (or at least that Twitter doesn't know about). The console.log(response) in the backend shows me that such a request to the Twitter api leads to a return message without place data:
{ data:
{ result: { places: [] },
query:
{ url: 'https://api.twitter.com/1.1/geo/search.json?query=FOOBARTOWN',
type: 'search',
params: [Object] } },
resp:
/*etc...*/
As you can see, places is an empty list. This response causes a crash in my frontend. I would like to know why. Look at the error message:
const handleError = function (error) {
if (error.json) {
> error.json().then(error => {
console.error('API Error:', error.message || error)
})
} else {
And some other console outputs in the browser:
GET http://localhost:3000/api/twitter/geocode?query=FOOBARTOWN 500 (Internal Server Error) (model.js:197)
Uncaught (in promise) SyntaxError: Unexpected token S in JSON at position 0
at handleError (model.js:214)
It seems like we are getting error 500. But why? My server hasn't failed. There's no error message in the node.js console.
Also, it seems like the program ends up in handleError, and then fails to convert the error to json.
Why does it go to handleError? What's the error?
Why does it say my server failed (error 500)?
How can I make sure that if this.geocode gets an empty message, I don't crash, and return that empty message (so I can check for an empty message and act appropriately in my React.js component)?
Why does it go to handleError? What's the error?
Your server is sending a 500 status code, with Something broke! as response body.
An when you try to use res.json() on a non JSON string you get:
Uncaught SyntaxError: Unexpected token S in JSON at position 0
The line if(error.json) is not doing what you think it does. handleError is being called when response.ok is false, since you're throwing the fetch response object otherwise, in that case error argument will be a fetch response that implements Body, which has a json method, even if the body isn't a JSON, which is your case.
Your handleError can be written like this, where you will handle fetch errors and non 2xx responses.
const handleError = async function(error) {
if(error instanceof Response) {
if(error.headers.get('content-type').includes('application/json'))
error = await error.json();
else error = await error.text();
}
console.error('API Error:', error.message || error)
}
Why does it say my server failed (error 500)?
Place a console.log(e) on Twitter.get().catch and you'll find out.
Your Twitter.get().then is also wrong, since you're sending the response twice.
if(response.date.places.length > 0){
res.send(response.data.result.places[0].bounding_box.coordinates[0][0]);
}
res.send()
Should be:
if(response.date.places.length > 0)
return res.send(response/*...*/);
res.send()

Returning error object along with the hapijs boom object

I'm trying to return the error object inside the response of a boom internal error object but it keeps omitting the error object.
I tried to follow the answer here but it didn't help.
function (request, reply) {
options.healthCheck(function (err) {
if (!err) {
return reply('I\'m healthy!!!');
}
var boomErr = boom.internal('I don\'t feel so good', {
err: err
});
boomErr.output.payload.details = err;
return reply(boomErr);
});
}
Here is the response:
{
"statusCode":500,
"error":"Internal Server Error",
"message":"An internal server error occurred",
"details":{ }
}
After digging into boom docs, I found out that all 5xx errors hide the custom message and payload.
Switching to bad request error solved my issue.

Categories