Mongoose: Way to handle mongoose.connect() error in promise catch handler

Created on 10 May 2016  路  31Comments  路  Source: Automattic/mongoose

How to handle mongoose.connect() error in catch handler? I want to use application initialization chain but can't do that because mongoose.connect() does not return rejected promise. It returns rejected promise only if I specify callback, but it's not a perfect solution.

Example:

mongoose.connect('mongodb://127.0.0.2/test') // if error it will throw async error
    .then(() => { // if all is ok we will be here
        return server.start();
    })
    .catch(err => { // we will not be here...
        console.error('App starting error:', err.stack);
        process.exit(1);
    });

Workaround:

mongoose.connect('mongodb://127.0.0.2/test', function() { /* dummy function */ })
    .then(() => {
        return server.start();
    })
    .catch(err => { // mongoose connection error will be handled here
        console.error('App starting error:', err.stack);
        process.exit(1);
    });

I think mongoose.connect() throws async error instead of return rejected promise in order to not break backward compatibility. Users expect that application will be finished with error code if something went wrong with mongoose connection establishment. If mongoose.connect() returns rejected promise application will be finished with 0 code and nothing will be output to console. So it will be good to have some way to say mongoose.connect() to return promise. Maybe something like exec():

mongoose.connect('mongodb://127.0.0.2/test').exec()
    .then(() => { // if all is ok we will be here
        return server.start();
    })
    .catch(err => { // if error we will be here
        console.error('App starting error:', err.stack);
        process.exit(1);
    });
can't reproduce

Most helpful comment

Just to confirm for anyone coming later, this works as expected:

mongoose.connect('http://127.0.0.1:27017/test')
  .then(() => {
    server.start();
  })
  .catch((err) => {
    console.log('Error on start: ' + err.stack);
    process.exit(1);
  });

All 31 comments

mongoose.connect('mongodb://localhost/dbCollection', function(err, db) {
    if (err) {
        console.log('Unable to connect to the server. Please start the server. Error:', err);
    } else {
        console.log('Connected to Server successfully!');
    }
});

@nasr18 What did you mean? :smiley: I don't want to use callback)

@Jokero sorry dude......... misunderstood your question. :)

Use the callback of mongoose.connect to catch any error during the connection.
You can start you server in the event open.

        mongoose.connection.once('open', function() {
            logger.info('MongoDB event open');
            logger.debug('MongoDB connected [%s]', url);

            mongoose.connection.on('connected', function() {
                logger.info('MongoDB event connected');
            });

            mongoose.connection.on('disconnected', function() {
                logger.warn('MongoDB event disconnected');
            });

            mongoose.connection.on('reconnected', function() {
                logger.info('MongoDB event reconnected');
            });

            mongoose.connection.on('error', function(err) {
                logger.error('MongoDB event error: ' + err);
            });

            // return resolve();
            return server.start();
        });

        return mongoose.connect(url, options, function(err) {
            if (err) {
                logger.error('MongoDB connection error: ' + err);
                // return reject(err);
                process.exit(1);
            }
        });

@mathieug I know, I can use callback for error handling but issue is about promises :smiley:

Don't use the promise from mongoose.connect, start your server when the connection is open for the first time (.once('open'))

Why? No difference at all. When connection is opened, mongoose.connect callback will be called as well as returned promise will be fulfilled and open event will be emitted. So you can use what you want

Yes but as you said, mongoose.connect needs a callback in case of error.

That's why I created this issue :smiley:

I have the same issue.

I'd like to be able to use mongoose.connect(...).catch(failCallback) but when an error occurs upon initial connection attempt failCallback does not execute. There's something wrong with the MongooseThenable pseudo-promise that mongoose.connect returns. Moreover I have configured mongoose.Promise = require('bluebird') and I really expect such async calls to return a _real_ promise, a Bluebird one in my case.

The problem with my failCallback not executing, however, happens only initially, i.e.:

// First kill mongod instance so that Mongoose cannot connect and start the app
mongoose.connect(uri, options).catch(failCallback); // failCallback does _not_ get called
// Then stop the app, start mongod instance so that Mongoose can connect, start the app
mongoose.connect(uri, options).then(successCallback); // successCallback gets called
// Then kill mongod instance so that Mongoose disconnects (while the app is running)
mongoose.connection.on('disconnected', function () {
  setTimeout(function () {
    mongoose.connect(uri, options).catch(reconnectFailCallback); // now reconnectFailCallback _does_ get called
  }, randomDelayOfAboutASecond);
});

But indeed it works like that (which is nonsensical):

// First kill mongod instance so that Mongoose cannot connect and start the app
var dummyNoopCallback = function () {};
mongoose.connect(uri, options, dummyNoopCallback).catch(failCallback); // failCallback _does_ get called

Why is that?

Re: returning a real promise, it's that way for backwards compatibility. Unfortunately people rely on .connect() chaining behavior for some reason (#3847, #3790, etc) so we're keeping it this way for now :) Added a fix so .catch() can catch initial connection issues.

It works! But will these changes break something for those who expect that application process will be finished when connection error is happened and there are no callback and promise handlers?

Example:

const mongoose = require('mongoose');
mongoose.Promise = Promise;

mongoose.connect('mongodb://127.0.0.2/test'); // no callback and promise handlers

Before these changes

/home/dmitry/example/node_modules/mongodb/lib/server.js:242
        process.nextTick(function() { throw err; })
                                      ^
Error: connect ECONNREFUSED 127.0.0.2:27017
    at Object.exports._errnoException (util.js:870:11)
    at exports._exceptionWithHostPort (util.js:893:20)
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1057:14)

With these changes process is finished with 0 code

Thanks for the suggestion, will investigate.

Cool! Thanks :+1:

+1
Would love to be able to return the result of .connect() as a full fledged Promise in the initialization chaining.

@CodeJjang does this not work already? This functionality has already been released

mongoose.connect('mongodb://127.0.0.2/test', function() { /* dummy function */ })
    .then(() => {
        return server.start();
    })
    .catch(err => { // mongoose connection error will be handled here
        console.error('App starting error:', err.stack);
        process.exit(1);
    });

This is not working for me :(
It show
(node:22564) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): MongoError: failed to connect to server [127.0.0.1:27017] on first connect

Hmm try getting rid of the callback in mongoose.connect()

I just got:

UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1):
MongoError: failed to connect to server [localhost:17551] on first connect

not using promises on mongoose 4.7.6 (latest right now), any way to avoid that Warning?
I'm using it with ExpressJS and if I use catch, it screws my logic and behaves weird as express-session is creating a MongoStore too... just messy.

Code sample please

I have the same issue as @matheo. Just opened this ticket:

https://github.com/christkv/mongodb-core/issues/163

Just to confirm for anyone coming later, this works as expected:

mongoose.connect('http://127.0.0.1:27017/test')
  .then(() => {
    server.start();
  })
  .catch((err) => {
    console.log('Error on start: ' + err.stack);
    process.exit(1);
  });

Just to confirm for anyone coming later, this works as expected:

mongoose.connect('http://127.0.0.1:27017/test')
  .then(() => {
    server.start();
  })
  .catch((err) => {
    console.log('Error on start: ' + err.stack);
    process.exit(1);
  });

I'm using this approach too! But when I stop mongodb then run this code, it's not working.

// mongoose version 5.4.14
mongoose
  .connect(databaseUri, {
    promiseLibrary: bluebird,
    useNewUrlParser: true,
    useFindAndModify: false,
    useCreateIndex: true,
    // Automatically try to reconnect when it loses connection to MongoDB
    autoReconnect: true,
    // Never stop trying to reconnect
    reconnectTries: Number.MAX_VALUE,
    // Reconnect every 500ms
    reconnectInterval: 500,
    // Maintain up to 10 socket connections. If not connected,
    // return errors immediately rather than waiting for reconnect
    poolSize: 10,
    // Give up initial connection after 10 seconds
    connectTimeoutMS: 10000,
  })
  .catch((err) => {
    console.log(err);
    process.exit(1);
  });

@nvtuan305 what do you mean by 'not working'? Is there some error messages?

@vkarpov15 I mean no error will be printed

Procedure:

  • Step 1: I stopped mongodb service
  • Step 2: Connect with mongodb. At this step, no error will be printed into console, the catch block will not been reached.
    // Never reach to here
    console.log(err);
    process.exit(1);

@nvtuan305 if initial connection times out, mongoose will report an error after connectTimeoutMS. Is that not the case for you? If so, what version of MongoDB and mongoose?

@vkarpov15

mongoose will report an error after connectTimeoutMS

In my case, mongoose doesn't report error after connectTimeoutMS at catch block. I'm using Mongodb 3.6.5 and mongoose 5.4.14

> mongod --version
db version v3.6.5
git version: a20ecd3e3a174162052ff99913bc2ca9a839d618
OpenSSL version: OpenSSL 1.0.2p  14 Aug 2018
allocator: system
modules: none
build environment:
    distarch: x86_64
    target_arch: x86_64

@nvtuan305 just to confirm, are you connecting to a standalone mongodb instance or a replica set?

@vkarpov15 I'm using standalone instance.

@nvtuan305 what operating system? Also, why do you expect Mongoose to fail to connect to the standalone - is the mongod instance down, is there no network connection, something else?

Below script works as expected:

const assert = require('assert');
const mongoose = require('mongoose');
mongoose.set('debug', true);

run().then(() => console.log('done')).catch(error => console.error(error.stack));

async function run() {
  console.log(mongoose.version);

  const start = Date.now();
  const opts = { useNewUrlParser: true, connectTimeoutMS: 1000 };
  await mongoose.connect('mongodb://doesntexist.com:27017/test', opts).
    catch(error => { console.log(`Caught "${error.message}" after ${Date.now() - start}`); });
}

Output

$ node gh-4135.js 
5.4.14
Caught "failed to connect to server [doesntexist.com:27017] on first connect [MongoNetworkError: connection 0 to doesntexist.com:27017 timed out]" after 1024
done
$ ^C

Hey, it seems basic but please double check theses instructions

  • Set your IP Adress on Mongo cluster, you can even set access from everywhere
  • Be sure to handle errors, with catch for example

I solve it doing that

Was this page helpful?
0 / 5 - 0 ratings