Catch an errors in ElectronJS [duplicate] - javascript

I'm currently building an application for file backup, and it got quite a bit of reading and writing to the filesystem. Most of it works great, but I'm struggling a bit with the error handling of the app.
In the screenshot below the last path is not a valid directory and return an exception as you can see.
function getTotalSize(pathToDir, dir) {
fs.readdir(pathToDir, function(err, files) {
if (err) {
// handle my error here
throw new Error('something bad happened');
return;
}
// continue if no errors :)
My question is, is it possible to replace the standard error window with my own? Or in some cases ignore the pop up of the error window? First time working with Electron so sorry if this is an obvious one.
Thanks!

When you throw the error from readdir it gets caught by the top-level uncaughtException handler, indicated by the the first line: "Uncaught Exception".
What you need to do is add your own custom handler for the uncaughtException in your main process and show whatever dialog you want from there.
Take a look at the dialog module.
As an example, you can use the dialog.showMessageBox method to configure all sorts of things about the error dialog like this:
process.on("uncaughtException", (err) => {
const messageBoxOptions = {
type: "error",
title: "Error in Main process",
message: "Something failed"
};
dialog.showMessageBoxSync(messageBoxOptions);
// I believe it used to be the case that doing a "throw err;" here would
// terminate the process, but now it appears that you have to use's Electron's
// app module to exit (process.exit(1) seems to not terminate the process)
app.exit(1);
});

Related

Do I need to restart my application after unhandledRejection

I have a nodeJS application (server) and using some 3rd party npm modules.
Also in my application, I have the following code:
process.on("unhandledRejection", (reason, promise) => {
console.error(`Unhandled Rejection at: ${promise} reason: ${reason}`);
restartApp(); // ← Is this a must action?
});
Seems like not all the promises are rejected properly, in the 3rd party modules and maybe also in my code.
I know it's the last resource to use this event handler.
Question after catching this unhandle rejection event, do I need to restart my application?
It's useful to divide errors into two broad categories: Operational and Programmer errors.
Operational errors are (unavoidable) run-time problems experienced by correctly-written programs, such as disk full, network connection loss etc.
Programmer errors are caused by bugs or oversights in code, that can not be handled since they will cause the program to enter an unknown state, they could be in your code or a module you're calling.
Best practice for programmer errors is to crash immediately. You should run your programs using a restarter (see below) that will automatically restart the program in the event of a crash. With a restarter in place, crashing is the fastest way to restore reliable service in the face of a transient programmer error.
If the error is an operational error, it may make sense to try to recover or re-try the operation (if this makes sense). There's no point retrying a REST request if you're getting 400 errors for example, but there might be if you're getting 500 errors (the system may recover).
See more here in this very useful guide:
https://www.joyent.com/node-js/production/design/errors
In your specific case, you're handling an unhandledRejection, this means that an application is in an undefined state... very similar to an unhandledException, the best thing to do is clean up anything that needs to be done and then exit or restart, also log the error (this is super important!, is the error happening every day, hour or minute?)
I'd suggest using a process monitor such as PM2 or Forever. These can auto-restart when you exit due to an error, and do lots of other cool stuff like logging these events.
Here's another nice guide from Heroku on the same topic:
https://blog.heroku.com/best-practices-nodejs-errors
The blogger (Julián Duque) has even put together some best practice on handling these events:
https://blog.heroku.com/best-practices-nodejs-errors#putting-it-all-together
This looks a little like so:
const http = require('http')
const terminate = require('./terminate')
const server = http.createServer(...)
const exitHandler = terminate(server, {
coredump: false,
timeout: 500
})
process.on('uncaughtException', exitHandler(1, 'Unexpected Error'))
process.on('unhandledRejection', exitHandler(1, 'Unhandled Promise'))
process.on('SIGTERM', exitHandler(0, 'SIGTERM'))
process.on('SIGINT', exitHandler(0, 'SIGINT'))
The Terminate module:
function terminate (server, options = { coredump: false, timeout: 500 }) {
// Exit function
const exit = code => {
options.coredump ? process.abort() : process.exit(code)
}
return (code, reason) => (err, promise) => {
if (err && err instanceof Error) {
// Log error information, use a proper logging library here :)
console.log(err.message, err.stack)
}
// Attempt a graceful shutdown
server.close(exit)
setTimeout(exit, options.timeout).unref()
}
}
module.exports = terminate
I think this style of managed, centralized handling of these events is the right way to go.

componentDidCatch + window.addEventListener('error', cb) don't behave as expected

I have a componentDidCatch error boundary on my root component, to render a nice error page in case something terrible happens.
But I also have a window.addEventListener('error', cb) event handler, in case something even worse happens (outside React).
My issue now is that whenever componentDidCatch gets an error, the window error handler gets it too, so I always end up with the worst case scenario error handling, rather than the "in-react" one.
Also, it looks like the global error handler is triggered before the React error boundary, so it's not possible to mark an error as "handled" so that I can manually ignore it later.
Here's a CodePen showing my issue:
https://codepen.io/FezVrasta/pen/MNeYqN
A minimal code example would be:
class App extends React.Component {
componentDidCatch(err) {
console.log('react handled error', err);
}
render() {
return 'foobar';
}
}
window.addEventListener('error', err => console.log('global error', err));
// on error, I get:
// 'global error', err
// 'react handled error', err
On the contrary, with just JavaScript, the local error handler would prevent the error from reaching the window error handler.
https://codepen.io/FezVrasta/pen/eqzrxO?editors=0010
try {
throw new Error('error');
} catch (err) {
alert('scoped error');
}
window.addEventListener('error', () => alert('global error'))
Is this expected behavior? Can you think of a way to make it work as I'd like it to?
Seems like this is just a development behaviour. Please take a look at this comment
In development environment, React uses a trick: caught exceptions are
thrown inside a fake DOM event which makes them reported by
window.onerror, but then React actually catches them so that they
don't propagate up (in case there is an error boundary — otherwise
they are rethrown).
In production errors caught by error boundaries stay caught.
I've just tested your example in production and alert does not show when error is thrown in render method.

Inspecting a caught error in a React Native app

I'm working with a React Native app in a dev environment and am running into an error whereby a toast is shown after I try to upload a file. The code is similar to the following:
function* onUploadPrompt(action: Action): Saga<*> {
try {
yield put(ProfileCertificateUpload.uploadEnd());
} catch (err) {
debugger;
// if upload fails show a toast to user so they may retry
yield put(Toast.showNegative(str.uploadFail()));
yield put(ProfileCertificateUpload.uploadEnd());
}
}
Note that I've set a debugger breakpoint in the catch block. If I stop the package manager and start it again (using yarn start) and go through the flow to trigger the error, I hit the breakpoint:
What puzzles me is that I don't see err anywhere; I can't inspect it. Shouldn't the err error variable be in scope where I set my breakpoint, so that I can have a look at the error and learn more about it?
Instead of debugger, put console.log(‘error:’, err); and check your console to see what the error actually is.

Writing thrown errors to a file in Javascript

Is there a way to add a default behavior in javascript to the throw event.
Like I am trying to add to a file whenever an error is thrown :
Example :
if (tables.length === 0) {
throw new Error("NO_TABLES_RETRIEVED");
}
I want to write to file first and then throw the error. I know I can add a function and just before throwing I can call that function but just to know more is there something like to add a default behavior with throw?
Error is different from Exception in Javascript w.r.t NodeJS. An error is any instance of the Error class. Errors may be constructed and then passed directly to another function or thrown. When you throw an error, it becomes an exception.2 Here's an example of using an error as an exception:
throw new Error('something bad happened');
but you can just as well create an Error without throwing it:
callback(new Error('something bad happened'));
Since you mentioned in the comment that you don't want to require the error handler file in all the files of the app. You can also make use of NodeJS EventEmitter module or use a error handler middleware
I think your best bet is to use a custom throw. I actually always do it, to be able to write
_throw("MyErrorMessage")
So I would do something like that:
//Should use something like dot-env with the path to your errors
require('dotenv').config()
//I'm just gonna fake it to make it work on your machine:
process.env.error_file_path = './myErrors.txt'
//throwLib.js
const util = require('util')
const fs = require('fs')
const writeFile = util.promisify(fs.appendFile)
_throw = (stringError) => {
writeFile(process.env.error_file_path, "Error : " + stringError + "\n").then(err => {
if (err)
console.log(err)
throw new Error(stringError)
}
)
}
//import this in your files.
//myFile.js
//someStuff
_throw('someMessage1')
_throw('someMessage2')
_throw('someMessage3')
You can create your own custom error and do some logic in the constructor.
function MyError(){
//do some logic herer (e.g. write to file)
console.log("some logic");
}
throw new MyError()
If you are using a NodeJS application, you can centrally manage all your thrown errors.
You should also name your errors:
class ApiError extends Error {
constructor (message, code) {
super(message);
this.name = 'ApiError';
this.code = code;
}
}
Similarly use other names for other error types.
For an express application,
app.use(routes);
// place this after your routes in express app. This will catch all your thrown errors.
app.use(function (err, req, res, next) {
console.log(err);
switch (err.name) {
case 'ApiError':
// write to file here
return res.status(err.code || 500).send({error: err.message});
case 'Some other error':
// handle differently
break;
default:
res.status(err.status || 500).render('500', {error: err});
}
});
Note if you are throwing from within a Promise, async/await, this won't catch your error. You will have to catch them centrally some other way, possibly by catching all Unhandled Promise Rejections in your app
process.on('unhandledRejection', (reason) => {
console.log(reason);
// log error in file
});
Similarly, you should also catch all uncaught exceptions centrally
process.on('uncaughtException', (err) => {
console.log(err);
// log error in file
});
Suggestion:
As far as logging errors in file are concerned, it is a great way to do that only if you're parsing your file by some other way, sending your file to logstash etc.
When you're not planning to parse your file programmatically, log your errors in an errordb / error-table. They are much easier to query that way :)

Why there is no error thrown with Strophe.js?

Example code:
var connection = null;
function onConnect(status) {
im_a_big_error.log('wtf');
// Why it doesn't throw me an error here ??
}
$().ready(function() {
connection = new Strophe.Connection('http://localhost:8080/http-bind');
connection.connect('admin#localhost', 'admin', onConnect);
});
It doesn't throw me an error in my Chrome console.
Do you have an idea to resolve this issue?
Yes, Strophe often catch errors by itself and currently doesn't provide any ability to get connection error information. While error catching is ok, the impossibility of catching errors by yourself is not very good. But you can fix it with the following code:
$().ready(function() {
connection = new Strophe.Connection('http://localhost:8080/http-bind');
connection._hitError = function (reqStatus) {
this.errors++;
Strophe.warn("request errored, status: " + reqStatus + ",
number of errors: " + this.errors);
if (this.errors > 4) this._onDisconnectTimeout();
myErrorHandler(reqStatus, this.errors);
};
connection.connect('admin#localhost', 'admin', onConnect);
});
where myErrorHandler is your custom connection error handler.
Yes, strophe swallows errors. Worse; After an error is thrown, the callback won't return true as it should, and strophe will remove the handler. As soon as an error occurs, the callback will never be called again.
I found the code from the current answer a bit hard to use. Internally, we use the following wrapper for every callback;
function callback(cb) {
// Callback wrapper with
// (1) proper error reporting (Strophe swallows errors)
// (2) always returns true to keep the handler installed
return function() {
try {
cb.apply(this, arguments);
} catch (e){
console.log('ERROR: ' + (e.stack ? e.stack : e));
}
// Return true to keep calling the callback.
return true;
};
}
This wrapper would be used as following in the code of the question;
connection.connect('admin#localhost', 'admin', callback(onConnect));
I've been playing with Strophe for a while now and I had to modify its default error handling routine to fit our needs
Strophe.js - log function - by default contains nothing - I added calls to my server side logging service for level === ERROR and level === FATAL
Strophe.js - run function - the default behavior for error is to remove the handler and to rethrow the error - since I already log the error server side I don't rethrow the error and decided to keep the handler (even if it failed). This behavior could make sense (or not) depending on your own implementation - since I use custom messages and have a rather complicated message processing routine I don't want the client to stop just because a message was not properly formatted when sent so I want to keep the handler, error or not. I replace the throw e line inside the run function with result = true;
Strope.js _hitError - as I mentioned, I don't want the client to ever disconnect so I rewrote the default behavior to never disconnect (no matter how high the error counter)
Hope these thoughts are of help to others - leave a comment if you have questions/want details.
I had a similar problem which I fixed using the approach given by tsds above. However with minimal modification. I created two connect methods one as connect and the other as connect_bak I placed the script
this.connection._hitError=function (reqStatus) {
client.connect_bak();
};
in my connectHandler function as well as the connect function. Such that the function is always binded on connect.

Categories

Resources