Electron ipcMain how to gracefully handle throwing an error - electron

In Electron if I throw an error anywhere on the backend it goes to a custom window. Trying to find a way to catch that to push to a custom area in my app I've found that I can detect the process with process.on('uncaughtException'). However I'm stuck trying to run a sender to send either the error or the report. What I've tried:
ipcMain.on('main', async (e, data) => {
try {
await someModule(data)
process.on('uncaughtException', err => e.sender.send('error', err.message))
return e.sender.send('audit', 'No issues found')
} catch (err) {
console.log(err)
}
})
module.js:
module.export = data => {
throw Error('this is a test')
}
In the above I'm sending both get both errorandaudit` to renderer. I've researched for a way to pass 'uncaughtException' to a ternary but I'm not able to find any docs on how to condition for 'uncaughtException' but I did try:
process.on('uncaughtException', err => {
if (err) return e.sender.send('error', err.message)
return e.sender.send('audit', 'test complete')
})
and the above only works if an error is present, research:
Catch all uncaughtException for Node js app
Nodejs uncaught exception handling
Node.js Uncaught Exception - Passing more details
In Electron how can I intercept the error to pass it to renderer from main without throwing the default error window?

If you use ipcMain.handle you will be able to handle errors in the renderer process like this
// Main process
ipcMain.handle('my-invokable-ipc', async (event, data) => {
await someModule(data)
return 'No issues found'
})
// Renderer process
async () => {
try {
const result = await ipcRenderer.invoke('my-invokable-ipc', data)
console.log(result) // 'No issues found' if someModule did not throw an error
} catch (err) {
// you can handle someModule errors here
}
}
Update: An issue with this approach is that the error emitted to the renderer process is serialized and it gets printed even though it's handled with a try/catch.
To fix this, you can also handle the errors in the main process
// Main process
ipcMain.handle('my-invokable-ipc', async (event, data) => {
try {
await someModule(data)
return 'No issues found'
} catch (err) {
// handle someModule errors and notify renderer process
// return err.message or any other way you see fit
}
})
// Renderer process
async () => {
const result = await ipcRenderer.invoke('my-invokable-ipc', data)
console.log(result) // 'No issues found' if someModule did not throw an error
}

Related

Dart async*, yield* and exception handling

If I have nested async* streams, exceptions do not appear to be catchable, which is counterintuitive.
An example:
void main() {
getString().listen(print);
}
Stream<String> getString() async* {
try {
yield* asyncStarError();
yield await asyncError();
} catch (err) {
yield 'Crash';
}
}
Stream<String> asyncStarError() async* {
throw Exception('A Stream error happened');
}
Future<String> asyncError() async {
throw Exception('A Future error happened');
}
This outputs:
Uncaught Error: Exception: A Stream error happened
Crash
So the exception thrown by asyncStarError is not caught, while the Future is caught as expected. Can anyone explain why?
You can observe the behaviour in dartpad: https://dartpad.dartlang.org
The yield* forwards all events from the stream it yields, including errors.
So, asyncStarError() produces a stream with an error event, and yield* forwards that error to the stream returned by getString, and then the getString.listen(print); does not add a handler so the error becomes uncaught.
(I'm guessing you're running this in a browser since that uncaught error doesn't crash the program.)
After that, the yield await asyncError() never yield anything. The await asyncError() itself throws, before reaching the yield, and that error is then caught by the catch which yields crash.
If you want to catch the errors of a stream, you need to actually look at the events, not just use yield* to forward them all blindly, including error events.
For example:
await for (var _ in asyncStarError()) {
// Wohoo, event!
}
would make the error from the asyncStarError stream an error at the loop. It would then be caught and you'd print "Crash".
TL;DR: The yield* operation forwards error events, it doesn't raise them locally.
Since yield * forwards them like #lrn said, you can catch them in main just fine. Or wherever you are consuming them.
void main() {
getString().listen(print).onError((e) => print('error caught: $e'));
}
Stream<String> getString() async* {
try {
yield* asyncStarError();
yield await asyncError();
} catch (err) {
yield 'Crash';
}
}
Stream<String> asyncStarError() async* {
throw Exception('A Stream error happened');
}
Future<String> asyncError() async {
throw Exception('A Future error happened');
}
this prints:
error caught: Exception: A Stream error happened
Crash
The difference is that async generator (async*) exceptions cannot be caught by surrounding it with try/catch.
Instead, you should use the callback handleError to achieve the behaviour you are looking for.
To go further, you may be interested in using runZonedGuarded.

What kind of errors are returned by HttpServer stream in Dart

I'm going through the Dart server documentation. I see I can await for an HttpRequest like this:
import 'dart:io';
Future main() async {
var server = await HttpServer.bind(
InternetAddress.loopbackIPv4,
4040,
);
print('Listening on localhost:${server.port}');
await for (HttpRequest request in server) {
request.response.write('Hello, world!');
await request.response.close();
}
}
That's because HttpServer implements Stream. But since a stream can return either a value or an error, I should catch exceptions like this, right:
try {
await for (HttpRequest request in server) {
request.response.write('Hello, world!');
await request.response.close();
}
} catch (e) {
// ???
}
But I'm not sure what kind of exceptions can be caught. Do the exceptions arise from the request (and warrant a 400 level response) or from the server (and warrant a 500 level response)? Or both?
Error status codes
On exception, a BAD_REQUEST status code will be set:
} catch (e) {
// Try to send BAD_REQUEST response.
request.response.statusCode = HttpStatus.badRequest;
(see source)
That would be 400 (see badRequest).
Stream errors
In that same catch block, the exceptions will be rethrown, which means that you will still receive all the errors on your stream. This happens in processRequest, which processes all requests in bind.
And you get the errors on your stream because they are forwarded to the sink in bind.
Kinds of errors
I could only find a single explicit exception type:
if (disposition == null) {
throw const HttpException(
"Mime Multipart doesn't contain a Content-Disposition header value");
}
if (encoding != null &&
!_transparentEncodings.contains(encoding.value.toLowerCase())) {
// TODO(ajohnsen): Support BASE64, etc.
throw HttpException('Unsupported contentTransferEncoding: '
'${encoding.value}');
}
(see source)
These are both HttpExceptions.

How to handle FileSystemException in dart when watching a directory

I have written a simple command line tool in dart that watches changes in a directory if a directory does not exits I get FileSystemsException.
I have tried to handle it using try and catch clause. When the exception occurs the code in catch clause is not executed
try {
watcher.events.listen((event) {
if (event.type == ChangeType.ADD) {
print("THE FILE WAS ADDED");
print(event.path);
} else if (event.type == ChangeType.MODIFY) {
print("THE FILE WAS MODIFIED");
print(event.path);
} else {
print("THE FILE WAS REMOVED");
print(event.path);
}
});
} on FileSystemException {
print("Exception Occurs");
}
I expect the console to print "Exception Occurs"
There are two possibilities:
The exception is happening outside this block (maybe where the watcher is constructed?)
The exception is an unhandled async exception. This might be coming from either the Stream or it might be getting fired from some other Future, like perhaps the ready Future.
You can add handlers for the async exceptions like this:
try {
// If this is in an `async` method, use `await` within the try block
await watcher.ready;
// Otherwise add a error handler on the Future
watcher.ready.catchError((e) {
print('Exception in the ready Future');
});
watcher.events.listen((event) {
...
}, onError: (e) {
print('Exception in the Stream');
});
} on FileSystemException {
print("Exception Occurs");
}
My guess would be it's an error surfaced through the ready Future.

Service worker sync event is not getting fired again after failure in first time

This is my code of sync event. Am I doing wrong by handling the promise of sync() method? Should not I handle promise which is inside event.waitUntil() method?
`self.addEventListener('sync', function(event) {
if (event.tag == "esssync") {
event.waitUntil(sync()
.then(function(data){
console.log(data);
try{
if (Notification.permission === 'granted'){
self.registration.showNotification("Sync success" + data);
}else{
console.log("Sync success");
}
}catch(err){
console.log("Sync success");
}
})
.catch(function(err){
console.log("Could not sync, scheduled for the next time");
}));
}
});`
Jake Archibald's guide to Background Sync is a great source of information; it contains the following:
[The promise passed to event.waitUntil indicates]
the success/failure of whatever it’s trying to do. If it
fulfills, the sync is complete. If it fails, another sync will be
scheduled to retry.
You're passing in a promise that always fulfills, because you have a .catch() function at the end of it, and you don't throw an error or return a rejected promise from within that .catch(). You just have a single console.log() statement.
The .catch() function is analogous to the catch block in traditional try {} / catch {} exception handling. You need to re-throw the original error from inside your catch {} block if you want it to propagate up the call stack, and the same thing applies with .catch() if you want the error to cause the promise to reject.
event.waitUntil(
sync()
.then(data => /* do something with data */)
.catch(error => {
console.log('Could not sync; scheduled for the next time', error);
throw error; // Alternatively, `return Promise.reject(error);`
}))
);
You can also just leave out the .catch() entirely, and rely on the fact that passing in the rejected promise to event.waitUntil() should log something in the JavaScript console by default.

google_oauth2_client how to handle 'Access denied'

Following the tutorial on DartWatch blog on using Google OAuth library. The question is how to handle: 'Access denied' error from Google ?
Here is my code example:
class Client
{
GoogleOAuth2 _auth;
Client()
{
_auth = new GoogleOAuth2(
'5xxxxxxxxxxxxxx.apps.googleusercontent.com', // Client ID
['openid', 'email', 'profile'],
tokenLoaded:oauthReady);
}
void doLogin()
{
// _auth.logout();
// _auth.token = null;
try
{
_auth.login();
}
on AuthException {
print('Access denied');
}
on Exception catch(exp)
{
print('Exception $exp occurred');
}
}
void oauthReady(Token token)
{
print('Token is: $token');
}
}
but I never hit catch block on any (!) exception. What I'm doing wrong ?
I'm using:
Dart Editor version 0.5.0_r21823
Dart SDK version 0.5.0.1_r21823
You never hit the catch block because auth.login is an asynchronous operation which returns a Future.
There is a great article on Future error handling on the dartlang.org website.
auth.login returns a Future immediately, but the work it does happens after control returns to the event loop (see my answer to another question for more on the event loop.)
Your code should look more like:
/// Returns a Future that completes to whether the login was successful.
Future<boolean> doLogin()
{
_auth.login()
.then((_) {
print("Success!");
return true;
}.catchError((e) {
print('Exception $e occurred.');
return false; // or throw the exception again.
}
}

Resources