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.
Related
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);
});
My script keeps raising this error. The code I've written is for testing Firestore security rules.
I basically wanted to test my firestore security while adding the javascript functions to it. It is possible to write the rules in firestore console and save them then and there but I wanted to use an IDE - VS code.
I wanted to know how to fix "FIRESTORE (7.14.3) INTERNAL ASSERTION FAILED: value must be undefined or Uint8Array". I've updated my npm and also tried an answer I found on Github which said for me to install first "npm i jest-environment-uint8array". It did not work. I have also tried to use " TextEncoder"
Like: var uint8array = new TextEncoder("utf-8").encode("Posts");
var string = new TextDecoder("utf-8").decode(uint8array);
It did not work as well. I would really appreciate it if you guys answered me thoroughly since I am just a novice.
My code for testing is
const { setup, teardown } = require("./helpers");
describe("General Safety Rules", () => {
afterEach(async () => {
await teardown();
});
test("should deny a read to the posts collection", async () => {
const db = await setup();
const postsRef = db.collection("Posts");
await expect(postsRef.get()).toDeny();
});
});
My helper js has basically the setup and the teardown for after when my code finishes.
const firebase = require("#firebase/testing");
const fs = require("fs");
module.exports.setup = async (auth, data) => {
const projectId = `rules-spec-${Date.now()}`;
const app = firebase.initializeTestApp({
projectId,
auth
});
const db = app.firestore();
// Apply the test rules so we can write documents
await firebase.loadFirestoreRules({
projectId,
rules: fs.readFileSync("firestore-test.rules", "utf8")
});
// write mock documents if any
if (data) {
for (const key in data) {
const ref = db.doc(key); // This means the key should point directly to a document
await ref.set(data[key]);
}
}
// Apply the actual rules for the project
await firebase.loadFirestoreRules({
projectId,
rules: fs.readFileSync("firestore.rules", "utf8")
});
return db;
};
module.exports.teardown = async () => {
// Delete all apps currently running in the firebase simulated environment
Promise.all(firebase.apps().map(app => app.delete()));
};
The error is:
jest ./spec
(node:8200) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
FAIL spec/collections.spec.js (10.284 s)
General Safety Rules
× should deny a read to the posts collection (4598 ms)
× should deny a write to users even when logged in (131 ms)
● General Safety Rules › should deny a read to the posts collection
FIRESTORE (7.14.3) INTERNAL ASSERTION FAILED: value must be undefined or Uint8Array
at fail (node_modules/#firebase/firestore/src/util/assert.ts:39:9)
at hardAssert (node_modules/#firebase/firestore/src/util/assert.ts:53:5)
at JsonProtoSerializer.fromBytes (node_modules/#firebase/firestore/src/remote/serializer.ts:250:7)
at JsonProtoSerializer.fromWatchChange (node_modules/#firebase/firestore/src/remote/serializer.ts:431:32)
at PersistentListenStream.onMessage (node_modules/#firebase/firestore/src/remote/persistent_stream.ts:568:41)
at node_modules/#firebase/firestore/src/remote/persistent_stream.ts:448:21
at node_modules/#firebase/firestore/src/remote/persistent_stream.ts:501:18
at node_modules/#firebase/firestore/src/util/async_queue.ts:358:14
● General Safety Rules › should deny a write to users even when logged in
FIRESTORE (7.14.3) INTERNAL ASSERTION FAILED: value must be undefined or Uint8Array
at fail (node_modules/#firebase/firestore/src/util/assert.ts:39:9)
at hardAssert (node_modules/#firebase/firestore/src/util/assert.ts:53:5)
at JsonProtoSerializer.fromBytes (node_modules/#firebase/firestore/src/remote/serializer.ts:250:7)
at JsonProtoSerializer.fromWatchChange (node_modules/#firebase/firestore/src/remote/serializer.ts:431:32)
at PersistentListenStream.onMessage (node_modules/#firebase/firestore/src/remote/persistent_stream.ts:568:41)
at node_modules/#firebase/firestore/src/remote/persistent_stream.ts:448:21
at node_modules/#firebase/firestore/src/remote/persistent_stream.ts:501:18
at node_modules/#firebase/firestore/src/util/async_queue.ts:358:14
console.error
[2020-05-20T13:13:11.851Z] #firebase/firestore: Firestore (7.14.3): FIRESTORE (7.14.3) INTERNAL ASSERTION FAILED: value must be undefined or Uint8Array
at Logger.defaultLogHandler [as _logHandler] (node_modules/#firebase/logger/src/logger.ts:115:57)
at logError (node_modules/#firebase/firestore/src/util/log.ts:45:20)
at fail (node_modules/#firebase/firestore/src/util/assert.ts:34:3)
at hardAssert (node_modules/#firebase/firestore/src/util/assert.ts:53:5)
at JsonProtoSerializer.fromBytes (node_modules/#firebase/firestore/src/remote/serializer.ts:250:7)
at JsonProtoSerializer.fromWatchChange (node_modules/#firebase/firestore/src/remote/serializer.ts:431:32)
Test Suites: 1 failed, 1 total
Tests: 2 failed, 2 total
Snapshots: 0 total
Time: 18.711 s
Ran all test suites matching /.\\spec/i.
(node:8200) UnhandledPromiseRejectionWarning: FirebaseError: Caught error after test environment was torn down
The client has already been terminated.
(node:8200) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 3)
(node:8200) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.
It seems to be a compatibility issue between the latest #firebase/testing#0.19.4 version and the internally used firebase#7.14.4 version. I had the same error and got it working with a temporary workaround.
Try downgrading your #firebase/testing version to ^0.15.0. After a quick npm i it worked for me. You can still upgrade to latest versions later as soon as they fix the current compatibility issues.
I will update my answer as soon as this happens.
Update:
This is the issue I created on the firebase-js-sdk GitHub page https://github.com/firebase/firebase-js-sdk/issues/3096.
Seems to be related to a bug in the testing framework Jest. The issue describes a workaround which does not require to downgrade.
I will update my answer as soon as the workaround is not required anymore.
Here's the Firebase issue:
https://github.com/firebase/firebase-js-sdk/issues/3096
Apparently it's caused by a bug in Jest ...
https://github.com/facebook/jest/issues/7780
... that includes this workaround:
https://github.com/facebook/jest/issues/7780#issuecomment-615890410
Steps to reproduce and fix ...
https://github.com/dconeybe/FirebaseJsBug3096
You can solve this problem simply by adding the following JSDOC comment to the top of your test file, which prevents Jest running your tests with JSDOM.
In spec/collections.spec.js:
/**
* #jest-environment node
*/
describe("my rules", () => {
// ...
});
The underlying issue is an incompatibility between jsdom and an internal assertion used by the firebase library, documented here: https://github.com/nodejs/node/issues/20978
The workaround noted by Tony O'Hagan (here) allows the tests to run in the JSDOM environment but the solution is pretty complex. If you're only testing Firestore security rules then you don't need JSDOM.
Update: Jest 27 (May 2021) changed Jest's default behavior to run in the Node environment instead of JSDOM
I'm trying to run a sample code from Github to insert the data in fire store database but I am getting an error.
Here is the error:
21:1 error Expected catch() or return promise/catch-or-return
21:35 error Each then() should return a value or throw promise/always-return a value or throw
Here is the code where I am getting the error:
// getting data
db.collection('cafes').get().then((snapshot) => { //----line 21
snapshot.docs.forEach(doc => {
renderCafe(doc);
})
});
Those look like lint warnings, not actual JS errors unless you're running this in some non-standard environment.
The first one appears to be telling you that you have no .catch() to handle errors from your db .get(). That's good advice. You must handle errors.
The second one appears to be wrong. There's no rule that you have to return a value from a .then(). If it's the end of the chain and you're done with the processing, there's no reason to return anything. You can probably make the second warning go away by putting a return 0 after renderCafe(doc), though personally I'd either stop using a tool that gives such bad advice or configure it not to warn on this issue because it's wrong and adding dummy, non-functional code just to make some tool be happy is not something I recommend.
On one end, I have a stream which may occasionally throw an error:
this.behaviorSubject.error(error)
Later on, however, I want to continue the stream:
this.behaviorSubject.next(anotherValue)
on the other end, I have a subscriber subscribed to behaviorSubject.asObservable().
In the subscribtion, I'm handling the value and the error:
.subscribe(
( value ) => { /* ok */ },
( error ) => { /* some error */ }
);
I want the effect to be the same as a simple onSuccess and onError callback, where onError is called every time an error occurs and doesn't prevent future onSuccess calls from being made. How do I do this with RXJS?
I've looked into catch but it seems to just prevent error from being called on subscribers.
Short answer: It's not possible.
How to work with this: The basic concept of RxJS is that any error or complete-call will basically "kill" a stream. This concept forces you not "just to throw around errors here and there as you please" but to handle errors and the flow of data within your application properly. A BehaviorSubject for example is typically meant to hold data, however it should not be used to also include the process of retrieving/creating that data and handle possible errors that might occur during the retrieval of the data.
So if you want to go by the book, you should split up your flow into two parts:
Retrieval/creation of the data: A stream, that will run once then then completes and/or throws an error whenever one occurs.
When the data is retrieved it will be sent to the store.
The store (e.g. as in your case: a bunch of BehaviorSubjects): Only valid data arrives in the store, this means that no error-handling is done here and all parts relying on the store can trust in the store that it holds the correct data.
As an example your data flow could look as follows (as a rough sketch):
store.ts
dataStore: BehaviorSubject<IData> = new BehaviorSubject<IData>();
errorMessage: BehaviorSubject<IErrorMsg> = new BehaviorSubject<IErrorMsg>();
data-retrieval.ts
fetchDataById(id: string) {
httpService.get(`some/rest/endpoint/${id}`)
.subscribe(handleData, handleError);
}
handleData(data: IData) {
errorMessage.next(null);
dataStore.next(data);
}
handleError(error: Error) {
errorMessage.next(error.message);
dataStore.next(null);
}
"But this looks like a lot of overhead..." - True, however it ensures a clean and easy-to-understand flow of data within your application, that is easy to test and maintain. Also there are ready-to-use store-concepts like ngrx or redux that could be used.
Rx is fundamentally built upon the concept that an observable is either active or finalized (onComplete or onError). When an Observable is finalizing it will unSubscribe from its upstream Observable. No .catch can fix that behaviour, it only gives you the option to map the error to something else.
Rx.Observable.interval(500)
.mergeMap(i => i % 3 == 2 ? Rx.Observable.throw(new Error('kboom')) : Rx.Observable.of(i))
.catch(err => Rx.Observable.of(err.message))
.subscribe(
val => console.log('val: ' + val),
err => console.log('err: ' + err),
() => console.log('stream completed')
)
Note that this example completes after 3 emissions instead of 5
When you invoke this.behaviorSubject.error(error) it wil internally finalize the Observable contained in your Subject. If you want to somehow emit errors then you need to make your errors non-error values:
this.behaviorSubject.next({ value: 'somevalue' });
this.behaviorSubject.next({ error: error });
this.behaviorSubject.next({ value: 'somevalue' });
Then you are able to distinguish based on the properties on your emitted value what action you should take.
This may not work for your situation, but I ran into this same issue when work with Angular 2 because we would navigate across screens and would want the service to retry an API and not just call the error function again. It would actually cause bigger issues because the function was called in our constructor and the error function would try to update the UI which was not yet ready.
What I did seems to work fine and be pretty clean. I created a reset the Subject in the error handler.
subject.subscribe(
( value ) => { /* ok */ },
( error ) => {
//handle error
//reset subject
this.subject = new Subject<any>();
}
);
This works in our case because every time you navigate to the screen new subscriptions are getting torn down from the old screen then set up in the new, so the new subject won't hurt anything.
As others have said, the error result is expected to be terminal.
Personally I think (in your case) there are 3 types of results (technically 4)
The ideal result is the success case that calls next().
A lethal fail (out of memory error or any other form of "call can't continue" error) should call error().
The third form and the one that is key to your problem is the non-terminal error. It is the "Result was not a success" form. Because you mean to continue, it is not an Error in the rxjs sense. It is merely another type of result. A result that says something else happened.
(The 4th form is "processing completed": done all I can and am exiting without error)
Now I'm not sure of the details, but as I recall typescript can handle union types (if not you might have to play with a result type of "any").
With Unions you can declare your object as (for example) Subject<string|failtype>
The point here is you can send different results from the next statement.
You'd do something like the following...
DoCoolThingFunction():Subject<string|failtype>
{
const response = new Subject<string|failtype>();
deeperPeriodicAsyncOperation.subscribe((result) => {
if (result is something I like) {
response.next(result.toString());
} else if (result is final value) {
response.next(result.toString());
response.complete();
} else if (result is something teminal) {
response.error(result);
} else if (result is non-terminal error) {
response.next(new failtype(result));
}
});
return response;
}
Basically, this is saying "An error in this range is non-terminal. As such it is not an error, it is just a different kind of operational data".
Of course it is up to your receiving code to determine which type of result it has been handed. I've no idea if there are any neat ways to do that. It'd be great if the result handler could have multiple different typed responses ((result:string) =>{}, (result:failtype)=>{}) etc. but that's not really a topic for this thread.
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.