How to await errors during meteor.startup?


#1

hi,

In my meteor startup on the server I want to check if I have all keys specified in the settings.json file. But although the test fails, I never see an error in my server logs, I want node to be killed… (otherwise the app is corrupted…)

IS THE AWAIT THE ISSUE HERE?

 Meteor.startup(async function() {
        console.log('------------------------------------');
        console.log('Validate settings.json parameters');
        console.log('------------------------------------');
        Meteor.absolutePath = path.resolve('.').split(path.sep + '.meteor')[0];
        console.log('Meteor tries to find the settings.json file in Meteor.absolutePath:', Meteor.absolutePath)
        var file = path.join(Meteor.absolutePath, 'settings-development-example.json');

        // READ THE FILE 
        var exampleSettingsFile = await fs.readJson(file);
        try {
            validateJSON(exampleSettingsFile)
        } catch (err) {
            throw new Error('Meteor wants to check your settings.json with the parameters in the example settings.json in the project root. Error: Cant read the example settings definitions file: ' + file);
        }

        var keysEqual = compareKeys(Meteor.settings, exampleSettingsFile);
        console.log('Settings file has all the keys as specified in the example json file?', keysEqual)
        if (!keysEqual) {
            console.error('Settings file incomplete, Please verify if you have all the keys as specified in the settings-development-example.json in the project root folder. In my dev environment: C:\Users\Qlikexternal\Documents\GitHub\QRSMeteor');
            throw new Error('Settings file incomplete, Please verify if you have all the keys as specified in the settings-development-example.json in the project root folder. In my dev environment: C:\Users\Qlikexternal\Documents\GitHub\QRSMeteor');
        }
    })

my console:

thanks!


#2

@robfallows any idea maybe?

How can I throw an error in a meteor startup function that uses await?

thanks


#3

Why are you using an async function? I can’t see any awaits?

What’s the issue?


#4

Hi, the Json file read is async

Should I just put try catch around the startup function?


#5

Do you mean it returns a Promise, or it’s a “traditional” asynchronous function? If it returns a Promise you should await validateJSON(exampleSettingsFile). If it doesn’t, then you’ll either need to wrap it in a Promise (if you want to use Meteor.startup(async function() {, or use Meteor.wrapAsync, if not (in which case you’ll need to ensure the validateJSON function has a signature of (error, result).

As to the original question of not being able to catch an exception, that’s because these are Promises under the covers and a characteristic of nested Promise based functions is that the underlying .catch() method is only available at the originating level. In async / await, a thrown exception just becomes a rejected Promise at that level of the Promise chain.

Searching google for “how to throw exceptions from async functions” has a number of results, some of which may help :wink:.

At the end of the day, I think @coagmano’s question is worth considering.


#6

Sorry guys, I tried to simplify the example but I made it worse… So I have updated the example with my real code. Basically I have a settings file, and I what to check that the users setting file includes all parameters(keys).

So I do try catch my settings file, that goes well.

But I would like to throw my own error:

 if (!keysEqual) {
            throw new Error('Settings file incomplete'); //this does not do anything...
        }

#7

Great, that helps. Where is the fs.readJson method from? That’s not a regular part of the fs library?

I’m guessing the issue is that promise rejections from Meteor.startup are being swallowed, so you may need to wrap your async function in your own promise or do things the old fashioned way with .then and .catch instead of await

EDIT:
I just looked up fs.readJson and I’m assuming it comes from the node-fs-extra package?
According to the docs, that doesn’t return a promise, and so the value is not awaitable.
It’s separate from the problem of Errors not stopping the app, but worth looking at


#8

thanks for you help! the FS is a bit distracting here… that works fine, but because I use FS, and I await it, I have an async meteor.startup function… and like you said above, then things willl become swallowed. so should I not just wrap meteor.startup in something? and catch any error raised in it?

so a simplified example will now be

 Meteor.startup(async function() {
      await someThing();
            throw new Error('OOPS');
        
    })

So is this normal or crazy?

 try {
        Meteor.startup(async function() {
           ....throw my error....
    } catch (error) {
        throw new Error(error);
    }

I want my node server to crash… so the admin can check the settings file.

thanks!


#9

Just did a quick test to see if it’s Meteor.startup or something else swallowing errors.

Test Code
Meteor.startup(() => {
  throw new Error('this is a test');
});
// Crashes server

Meteor.startup(async () => {
  throw new Error('this is a test');
});
// Swallows promise rejection

async function asyncFunctionThatThrowsError () {
  throw new Error('This is a test')
}
Meteor.startup(async () => {
  await asyncFunctionThatThrowsError();
});
// Swallows promise rejection

(async function asyncIIFEThatThrowsError() {
  throw new Error('This is a test');
})().catch(process.exit.bind(null,1))
// Swallows promise rejection

(async function asyncIIFEThatThrowsError() {
  throw new Error('This is a test');
})().catch(process.exit.bind(null, 1))
// Crashes server manually

It looks like unhanded promise rejections in general are being swallowed in Node

Updating to Meter 1.6-rc (Node 8) outputs a warning to terminal but still doesn’t crash:

Terminal output
meteor-async-error-test: updated to Meteor 1.6-rc.4.

2017-10-09 09:16:15 - ~/development/meteor-async-error-test
○ → meteor
[[[[[ ~/development/meteor-async-error-test ]]]]]

=> Started proxy.
=> Started MongoDB.
W20171009-09:17:35.718(11)? (STDERR) (node:6343) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: This is a test
W20171009-09:17:35.749(11)? (STDERR) (node:6343) [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.
W20171009-09:17:35.750(11)? (STDERR) (node:6343) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: this is a test
W20171009-09:17:35.750(11)? (STDERR) (node:6343) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 4): Error: This is a test
=> Started your app.

=> App running at: http://localhost:3000/

I looked up how to force these to exit and you can add this bit of code to the top of your server code to do so:

process.on('unhandledRejection', up => { throw up })

Which works on Meteor 1.5 and 1.6


#10

thanks a lot! I will try, so I also created a test, and in this way I can pick up the error:

 try {
        Meteor.startup(async function() {
           ....throw my error....
    } catch (error) {
        throw new Error(error);
    }

results in:
(STDERR) Settings file incomplete, Please verify if you have all the keys as specified in the settings-development-example.json in the project root folder. In my dev environment: C:UsersQlikexternalDocumentsGitHubQRSMeteor

#11

cool stuff… so my meteor dev client tries to re-run 3 times before it stops, I have never seen the benefit of this, is this also something we can kill? Normally an error will not fix it self? (maybe for time-outs it does…)