vue-resource: catch "Uncaught (in promise)" when intercepting an ajax error - javascript

I'm using vue-resource to fetch data from the server. A user needs to have a JWT token to get the correct data. If the token is invalid or expired, a 401 status is returned. If the user tries to access a forbidden page, a 403 is returned.
I would like to catch those errors and handle them appropriately (globally). This means, that the calls should be completely handled by the interceptor (if 401, 403).
How can I prevent the browser message "Uncaught (in promise)" and create some global error handling? I don't want to have a local error handler on every call.
I have the following interceptor:
Vue.http.interceptors.push(function (request, next) {
request.headers.set('Authorization', Auth.getAuthHeader());
next(function (response) {
if (response.status === 401 || response.status === 403) {
console.log('You are not logged in or do not have the rights to access this site.');
}
});
});
And the following call in the Vue methods:
methods: {
user: function () {
this.$http.get('http://localhost:8080/auth/user').then(function (response) {
console.log(response);
});
}
}

This is a bit of a dilemma, isn't it. You don't want unhandled promise rejections to get swallowed, because it means your application might not function and you won't know why, and will never get a report of the error happening.
On the other hand, it's silly to use the exact same error handling mechanism for every single .catch() statement in your application, so implementing a global error handler is definitely the way to go.
The problem is that in most cases, you will have to re-throw the error from your global error handler, because otherwise your application will think the request went through ok and will proceed to process the data, which will not exist.
But this leads to the situation where the Uncaught (in promise) error shows up, because the browser will think you didn't handle the error, whereas in reality you did, in your global error handler.
To get around this, there is now the onunhandledrejection event, and you can use that to prevent the browser from logging these errors, but then you have to make sure you process them yourself.
So what we often do is have our own error classes, and when a response error is thrown, we convert the error to one of our error classes, depending on the HTTP status code.
We also append a property to this error, something like ignoreUnhandledRejection and set it to true. Then, you can use the global handler to filter out those errors and ignore them, because you know that you have already handled them globally:
/**
* Prevent logging already processed unhandled rejection in console
*/
window.addEventListener('unhandledrejection', event => {
if (event.reason && event.reason.ignoreUnhandledRejection) {
event.preventDefault();
}
});

Related

What can cause fetch() to return success, but subsequently accessing the body to throw TypeError or AbortError?

I use fetch for getting some resources from the server. I can see from logs that from time to time obtaining the JSON body fails on "TypeError: Failed to fetch", which is very interesting because this type of error should only happen when the request fails.
The simplified code I use:
const response = await fetch('https://something.com/api')
try {
// below throws TypeError: Failed to fetch in Chrome/Safari
// and AbortError in Firefox
await response.json();
} catch(error) {
console.log('error happened', error);
}
I cannot really find a case when this might happen. I tested a number of plausible scenarios and all failed on the first line of code, i.e. fetch('https:/something.com/api'). I have no idea when this might happen. I also should mention that it happens in modern browsers like Chrome 99, so it is not definitely something like Internet Explorer thing.
I found this useful example and it shows that requests are cancelled when you unload the document. Obviously, cancellation happens on the fetch line but I decided to stop logging these errors when the document is unloaded/hidden. Even when these cases are not logged I can see it happens on visible documents too.
https://github.com/shuding/request-cancellation-test
Cases tested:
Network error - user disconnected from the internet
CORS - missing CORS headers
Obviously, this does not prove anything but people in the comments think that I do something wrong in implementation and do not trust me when I say it indeed happens when obtaining the JSON body. This is the information I am able to log when I catch the error. Most properties are from the Response object.
This is the log captured from visitor using Chrome 100. Firefox does not throw TypeError instead it throws AbortError but it also happens when converting to json.
This looks like a network error that happened after the response HTTP headers have already been received. In such a situation, the fetch method returns success, but subsequent attempts to access the response body may fail.
I managed to trigger this kind of error with this simple server:
#!/usr/bin/env python3
import http.server
import time
class Handler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
self.wfile.write(b'HTTP/1.0 200 OK\r\n')
self.wfile.write(b'Content-type: text/html\r\n')
self.wfile.write(b'\r\n')
self.wfile.write(b"""\
<script type="module">
const f = await fetch("/j");
try {
await f.json();
} catch (e) {
alert(e);
}
</script>
""")
return
self.wfile.write(b'HTTP/1.0 200 OK\r\n')
self.wfile.write(b'Content-encoding: gzip\r\n') # bogus
self.wfile.write(b'Content-type: application/json\r\n')
self.wfile.write(b'\r\n')
self.wfile.write(b'[]')
server = http.server.HTTPServer(('127.0.0.1', 31337), Handler)
server.serve_forever()
It introduces a deliberate framing error when serving the JSON response; the headers indicate that gzip compression is employed, but no compression is actually used. When I open http://127.0.0.1:31337/ in Chromium 100.0.4896.127, it displays the following alert:
TypeError: Failed to fetch
Firefox ESR 91.8.0 displays a marginally more helpful:
TypeError: Decoding failed.
The particular framing error demonstrated above is rather contrived, and I doubt it is exactly the kind that the asker experienced. But the fact that it appeared in the middle of the response body is probably at the heart of the described problem.
The specific pair of error types from the question can be triggered by modifying the server thus:
self.wfile.write(b'HTTP/1.1 200 OK\r\n')
self.wfile.write(b'Content-type: application/json\r\n')
# sic: larger than the actual body length
self.wfile.write(b'Content-length: 2\r\n')
self.wfile.write(b'\r\n')
self.wfile.write(b'[')
This displays the same alert:
TypeError: Failed to fetch
in Chromium (same version), and
AbortError: The operation was aborted.
in Firefox (also same).
As such, a likely cause would be a flaky connection dropped in the middle of receiving the body. The browsers’ errors are not particularly detailed here (and sometimes are downright misleading), so we are resigned to speculate, but this seems like the best guess.
Your error from this line:
const response = await fetch('https://something.com/api')
I removed await response.json() to test, the error still exists.
Please look console tab in DevTool, make sure that you use this setting
These are 2 example errors Failed to fetch:
(async () => {
try {
const response = await fetch('http://my-api.com/example')
await response.json()
} catch (e) {
console.log('e')
console.log(e)
}
})()

Can We Explicitly Catch Puppeteer (Chrome/Chromium) Error net::ERR_ABORTED?

Can we explicitly and specifically catch Puppeteer (Chromme/Chromium) error net::ERR_ABORTED? Or is string matching the only option currently?
page.goto(oneClickAuthPage).catch(e => {
if (e.message.includes('net::ERR_ABORTED')) {}
})
/* "net::ERROR_ABORTED" occurs for sub-resources on a page if we navigate
* away too quickly. I'm specifically awaiting a 302 response for successful
* login and then immediately navigating to the auth-protected page.
*/
await page.waitForResponse(res => res.url() === href && res.status() === 302)
page.goto(originalRequestPage)
Ideally, this would be similar to a potential event we could catch with page.on('requestaborted')
I'd recommend putting your api calls and so in a trycatch block
If it fails, you catch the error, like you are currently doing. But it just looks a bit nicer
try {
await page.goto(PAGE)
} catch(error) {
console.log(error) or console.error(error)
//do specific functionality based on error codes
if(error.status === 300) {
//I don't know what app you are building this in
//But if it's in React, here you could do
//setState to display error messages and so forth
setError('Action aborted')
//if it's in an express app, you can respond with your own data
res.send({error: 'Action aborted'})
}
}
If there are not specific error codes in the error responses for when Puppeteer is aborted, it means that Puppeteer's API has not been coded to return data like that, unfortunately :')
It's not too uncommon to do error messages checks like you are doing in your question. It's, unfortunately, the only way we can do it, since this is what we're given to work with :'P

Clarifying "400 - Bad Request"

I am triggering a bad request on purpose from my backend. The backend using express is answering properly as expected with:
[...]
.catch((error) => {
res.statusCode = 400;
res.json({
msg: "This is some error",
err: error,
});
});
My question is: Is the 400 always showing up in browser console? I thought I handled the error the right way.
And why do I have to retrieve the data in the frontend with
// 400:
function (data) {
var data = data.responseJSON.msg);
}
instead of (similar to 200:)
// 400:
function (data) {
var data = data.msg);
}
I am just wondering, if I do something wrong.
Thanks for any advice
When fetching data asynchronously, any response other than a 2** response code gets handled by a .catch block. So any other response whether it be 4** or 5** gets caught by the catch block and for that reason if you're using a library like Axios or the likes the response from the backend will be in the error.response or data.response in your case.
I wasn't aware of what you're using to carry out the asynchronous fetching of data i.e. Fetch Api so my answer is a generic response and also is the above code complete?
In my experience any error from whatever weither it be am asynchronous call that generates an error always gets logged to the console, but you can from the front end handle those errors in the catch block
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 or in your case the .responseJSON
By catching your error with an catch block .catch() or if you using async / await you should, usually, wrap the code inside of an try / catch to catch the error otherwise it will always output this red error in the console

How to handle can-connect errors correcly when connecting to a RESTful API

I've managed to load data and to save data. But cannot understand the error handling scheme needed.
When everything goes fine I receive the same object in that was sent but with an extra attribute _saving (false).
When something goes wrong, for instance try to store a string instead of a number, I'll get:
Bad request (error on the console, don't want that)
The response object (might be usefull to show an error)
"Uncaught (in promise)" error
Example:
Code:
this.save()
.then(function(result) {
console.log('ok1', result);
}).catch(function() {
console.log('errorHandler1');
});
OK:
Error:
I've been trying to use catch on promises, following this guidelines:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch
but had no luck at all.
This should should work buy just changing p1.then to thisObjectThatIWantToSave.save.then but it didn't.
p1.then(function(value) {
console.log(value); // "Success!"
throw 'oh, no!';
}).catch(function(e) {
console.log(e); // "oh, no!"
}).then(function(){
console.log('after a catch the chain is restored');
}, function () {
console.log('Not fired due to the catch');
});
Again, it still stores the information when the data is correct, the problem I see is that I don't have the tools to decide when was the data correctly stored or not and to avoid triggering errors that can be correctly handled.
I'm using
canjs v3.0.0-pre.11
a restful API provided by feathers
Regarding the error handling ...
Bad request (error on the console, don't want that)
There's no way of preventing the error on the console. This is something chrome does.
The response object (might be usefull to show an error)
You can read the reason for the rejection in can-stache like {{promise.reason}}.
"Uncaught (in promise)" error
I'm not sure why this is being thrown as clearly, your catch is being hit. If you change it to:
this.save()
.then(function(result) {
console.log('ok1', result);
},function() {
console.log('errorHandler1');
});
Do you get the same behavior?

Error conditions for NPM Request lib

An error is being passed by the callback in my request function. I am trying to determine under what conditions an error is passed to the callback.
var request = require('request');
request('http://www.google.com', function (error, response, body) {
if(error){
//why or when would an error be created?
}
else if (response.statusCode == 200) {
console.log(body) // Show the HTML for the Google homepage.
}
else{
// when would this happen ?
}
})
the documentation doesn't seem to cover what conditions will cause an error object to be created and passed. Right now I just assume anything but a 200 or 300 will cause an error to be created, but I am just guessing.
request library uses the node.js http module internally for making GET request. From it's doc:
If any error is encountered during the request (be that with DNS
resolution, TCP level errors, or actual HTTP parse errors) an 'error'
event is emitted on the returned request object.
I guess you have to go though the http module source to exactly find out what are the errors.

Categories

Resources