Search code examples
javascriptnode.jsexpressgridfs-stream

Error cannot be caught with Express.js and gridfs-stream


It's an easy image (picture) download server, Express.js receives the request, gets an image from MongoDB GridFS, and responds with the file.

It's OK when request is valid (when the requested file exists).

The problem is that I cannot catch the MongoError when the query failed (i.e. requested image does not exist).

import Grid from 'gridfs-stream'
const root = 'fs_images'

// This func returns the file stream
export function getReadStream (id) {
    const gfs = Grid(mongoose.connection.db, mongoose.mongo)
    const options = {
        _id: id,
        mode: 'r',
        root: root
    }
    const readStream = gfs.createReadStream(options)
    readStream.on('error', function (err) {
        // throw here
        // it performs the same without this on-error hook;
        // if comment the `throw err`, nothing will happens
        // but I want the caller knows the error
        throw err
    })
    return readStream
}

And this is the router

router.get('/:fileId', function (req, res, next) {
    const fileId = req.params.fileId
    try {
        const imgReadStream = image.getReadStream(fileId)
        imgReadStream.pipe(res)
    } catch (err) {
        // nothing catched here
        // instead, the process just crashed
        console.log(err)
    }
}

And I just cannot catch the err. When I try to request something that doesn't exist the MongoError shows in the console, and the app crashes with errno is 1.

Head of console output:

/.../node_modules/mongodb/lib/utils.js:123
process.nextTick(function() { throw err; });
                              ^
MongoError: file with id 123456123456123456123456 not opened for writing
at Function.MongoError.create (/.../node_modules/mongodb-core/lib/error.js:31:11)

This may be a bit different. If somewhere else throws an Error it will be caught by my error-handler (app.use(function(err, req, res, next){ /* ... */})), or at least by the default handler from Express.js, and returns a 500, without the process crashing.

In short, I want the app to know and catch this MongoError so I can handle it manually (i.e. return a 404 response).


Solution

  • try/catch won't work because the error is happening in a different tick (asynchronously). Perhaps you could listen for the error in the router instead?

    const imgReadStream = image.getReadStream(fileId)
    
    imgReadStream.on('error', function(err) {
        // Handle error
    });
    
    imgReadStream.pipe(res)