Custom Error not propagated properly on throw statement or next() call
Setting name
attribute of the class in its constructor solves the problem (line no. 3):
class APIError extends Error constructor: -> @name = 'APIError'app.use (err, req, res, next) -> if err instance of APIError console.log 'Error captured'app.get '/', (req, res, next) -> #This will work: captured by error handler throw new APIErrorapp.get '/in-mongoose-callback', (req, res, next) -> UserModel.where('name', 'Joe').findOne (err, doc) -> #This one works as expected: captured by error handler return next new APIError
This is due to one of the changes that landed in CoffeeScript 1.3.3 (MAY 15, 2012):
Due to the new semantics of JavaScript's strict mode, CoffeeScript no longer guarantees that constructor functions have names in all runtimes. See #2052 for discussion.
Note that using throw
statement instead of next()
inside a Mongoose query callback will not work.
While using express, I extend Error, set the name as prototype property, and perform a stack trace.
class RequestError extends Error name: 'RequestError' constructor:(@status, message)-> super message Error.captureStackTrace @, @constructor
I can do new RequestError 403, 'Invalid username/password.'
for a bad user signin, or new RequestError 404, 'Page not found.'
for a bad page request. When I catch the error I do
require 'colors'console.error error.stack.red # logs where any error occuredif error instanceof RequestError # proper response for user errors response.status(error.status).send(error.message)else response.status(500).send('Internal Server Error') # for 'other' errors the user only gets back a 500 'Internal Server Error'
It's hard to tell but we had issues on errors until we moved them to the end of the server script, even after the start server. This is using Express with Node, but could give you a hint. Before we had this near the beginning of server file assuming no issues but started working after we moved all our error handling to end. Unsure if router or middleware of framework order of operations issue but seemed to fix our issues.
/////////////////////////////// Start Server/////////////////////////////app.listen(siteConf.port);//////////////////////////// Error Handling////////////////////////// function NotFoundError(req, message){ this.name = "NotFoundError"; this.status = 404; this.message = message || (req.method + " " + req.url + " could not be found"); Error.captureStackTrace(this, NotFoundError);}NotFoundError.prototype.__proto__ = Error.prototype;function ProtectedResourceError(req, message){ this.name = "ProtectedResourceError"; this.status = 401; this.message = message || (req.url + " is protected");}ProtectedResourceError.prototype.__proto__ = Error.prototype;function ValidationError(req, message, errors){ this.name = "ValidationError"; this.status = 400; this.message = message || ("Error when validating input for " + req.url); this.errors = errors;}ValidationError.prototype.__proto__ = Error.prototype;function SearchError(req, message){ this.name = "SearchError"; this.status = 400; this.message = message || ("Error when searching");}SearchError.prototype.__proto__ = Error.prototype;// If no route matches, throw NotFoundErrorapp.use(function(req, res, next) { console.log("No matching route for " + req.url); next(new NotFoundError(req)); });// General error handlerapp.use(function(err, req, res, next) { //console.log("Trapped error : " + err); // handle exceptions if (err instanceof ValidationError) { standardResponse(req, res, {error: err.message, fields: err.errors}, err.status, err.message); } else if (err instanceof ProtectedResourceError) { standardResponse(req, res, {error: err.message}, err.status, err.message); } else if (err instanceof NotFoundError) { standardResponse(req, res, {error: err.message}, err.status, err.message); } else if (err instanceof SearchError) { standardResponse(req, res, {error: err.message}, err.status, err.message); } else { standardResponse(req, res, {error: err.message}, 500, err.message); }});