bcrabbe
bcrabbe

Reputation: 43

Node Express ejs Error: Failed to lookup view "error" in views directory

I am making an express app with ejs and mongoose.

I am getting this error:

 Error: Failed to lookup view "error" in views directory "/Users/ben/Documents/csMSc/web/site/app/views"
at EventEmitter.app.render (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/application.js:555:17)
at ServerResponse.res.render (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/response.js:938:7)
at module.exports (/Users/ben/Documents/csMSc/web/site/app/app.js:94:7)
at Layer.handle_error (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/layer.js:58:5)
at trim_prefix (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/index.js:300:13)
at /Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/index.js:270:7
at Function.proto.process_params (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/index.js:321:12)
at IncomingMessage.next (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/index.js:261:10)
at fn (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/response.js:933:25)
at EventEmitter.app.render (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/application.js:557:14)
at ServerResponse.res.render (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/response.js:938:7)
at app.use.res.render.message (/Users/ben/Documents/csMSc/web/site/app/app.js:83:9)
at Layer.handle_error (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/layer.js:58:5)
at trim_prefix (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/index.js:300:13)
at /Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/index.js:270:7
at Function.proto.process_params (/Users/ben/Documents/csMSsc/web/site/app/node_modules/express/lib/router/index.js:321:12)   

from two calls to res.render() where the data that is passed in comes back from a mongoose query e.g.:

  if(req.query.author !== undefined) {
        var author = req.query.author;
        Post.find().where('author').equals(author).sort({ created: -1 }).limit(10).exec(function(err, authorsPosts) {
            if (err) return res.send("error");
            if(authorsPosts.length==0) {
                res.render('pages/index', {
                    viewDataSignStatus: viewDataSignedIn[signedIn],
                    previews: authorsPosts,
                    error: "Sorry there are no posts with that tag."
                });
            } else {
                res.render('pages/index', {
                    viewDataSignStatus: viewDataSignedIn[signedIn],
                    previews: authorsPosts
                });
            }
        });
    }

and the other one is the same but with a query of

    Post.find( { tags : { $elemMatch: { $in : tagList } } } ).limit(10).exec(function(err, taggedPosts) {

However all my other render calls on this view are working just fine including one later in the same function:

 //or just latest
    Post.find().sort({ created: 1 }).limit(10).exec(function(err, latestPosts) {
        if (err) return res.send(err);
        res.render('pages/index', {
            viewDataSignStatus: viewDataSignedIn[signedIn],
            previews: latestPosts
        });
    });

where Im pretty sure latestPosts is in exactly the same format as authorsPosts in the one above.

There are no calls to render a view called error.

The error data passed to some of the calls to res.render('pages/index') above is passed using a custom filter

//custom ejs filter, sets to default value if data not supplied
ejs.filters.get = function(obj, prop, def) {
  return obj[prop] === undefined ? def : obj[prop];
};

which appears in the file app/views/pages/index.ejs as

<p><%=: locals | get:'error','' %> </p>

My ejs setup looks like:

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.engine('ejs', require('ejs').renderFile);

heres the whole offending route function in all its horrible glory

router.get('/', function(req, res, next) {
    var accountController = new AccountController(User, req.session);
    console.log("here1");
    var signedIn = accountController.session.userProfileModel !== undefined ? 1 : 0;
    console.log("here2");

    //Author search
    if(req.query.author !== undefined) {
        var author = req.query.author;
        Post.find().where('author').equals(author).sort({ created: -1 }).limit(10).exec(function(err, authorsPosts) {
            if (err) return res.send("error");
            console.log("\n\nAuthorsPosts:" +authorsPosts);
            console.log("\n\authorsPosts.length: " +authorsPosts.length);
            console.log("authors post.constructor = " +authorsPosts.constructor);
            if(authorsPosts.length==0) {
                console.log("length=0");
                res.render('pages/index', {
                    viewDataSignStatus: viewDataSignedIn[signedIn],
                    previews: authorsPosts,
                    error: "Sorry there are no posts with that tag."
                });
            } else {
                res.render('pages/index', {
                    viewDataSignStatus: viewDataSignedIn[signedIn],
                    previews: authorsPosts
                });
            }
        });
    }
    //Tag search
    if(req.query.filter !== undefined) {
        var tagList = req.query.filter.constructor == Array ? req.query.filter : req.query.filter.split(",");
        Post.find( { tags : { $elemMatch: { $in : tagList } } } ).limit(10).exec(function(err, taggedPosts) {
            if (err) return res.send("error");
            console.log("\n\taggedPosts.length: " +taggedPosts.length);
            if(taggedPosts.length==0) {
                console.log("length=0");

                res.render('pages/index', {
                    viewDataSignStatus: viewDataSignedIn[signedIn],
                    previews: taggedPosts,
                    error: "Sorry there are no posts with that tag."
                });
            } else {
                console.log("\n\ntaggedPosts:\n\n" +taggedPosts);
                res.render('pages/index', {
                    viewDataSignStatus: viewDataSignedIn[signedIn],
                    previews: taggedPosts
                });
            }
        });
    }
    //or just latest
    Post.find().sort({ created: 1 }).limit(10).exec(function(err, latestPosts) {
        if (err) return res.send(err);
        res.render('pages/index', {
            viewDataSignStatus: viewDataSignedIn[signedIn],
            previews: latestPosts
        });
    });
});

Whats more, it isn't totally not working. When the code gets to those render calls it throws the error and the page usually freezes, you cant click any links, then if I reload once or twice it will work, and render the template with the correct data.

Also when I travel to '/' with on of these query strings e.g. GET /?filter=Marc%20Behrens it gets as far printing all the returned posts and then throws the error.

Thank you!

Edit: thanks Alex Ford.

new error is:

Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (http.js:690:11)
    at ServerResponse.header (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/response.js:700:10)
    at ServerResponse.send (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/response.js:154:12)
    at ServerResponse.json (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/response.js:240:15)
    at ServerResponse.send (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/response.js:142:21)
    at module.exports (/Users/ben/Documents/csMSc/web/site/app/app.js:100:9)
    at Layer.handle_error (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/layer.js:58:5)
    at trim_prefix (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/index.js:300:13)
    at /Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/index.js:270:7
    at Function.proto.process_params (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/index.js:321:12)
    at next (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/index.js:261:10)
    at Layer.handle_error (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/layer.js:60:5)
    at trim_prefix (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/index.js:300:13)
    at /Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/index.js:270:7
    at Function.proto.process_params (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/index.js:321:12)
    at next (/Users/ben/Documents/csMSc/web/site/app/node_modules/express/lib/router/index.js:261:10)

Upvotes: 4

Views: 15771

Answers (4)

ChevCast
ChevCast

Reputation: 59163

My guess for the "headers already sent" error is that your // or just latest code is running even when one of the above if statements runs. If that's the case then you'll surely be making multiple calls to res.render or res.send. Try this:

router.get('/', function(req, res, next) {

  /* ... */

  //Author search
  if(req.query.author !== undefined) {
    /* ... */
      if(authorsPosts.length==0) {
        res.render(/*...*/);
      } else {
        res.render(/*...*/);
      }
    /* ... */
  }
  //Tag search
  else if(req.query.filter !== undefined) {
    /* ... */
      if(taggedPosts.length==0) {
        res.render(/*...*/);
      } else {
        res.render(/*...*/);
      }
    /* ... */
  }
  //or just latest
  else {
    res.render(/*...*/);
  }
});

Upvotes: 3

adongo
adongo

Reputation: 125

what i didn't do right is omitting the extension .html in the routes directory.

router.get('/', function(req, res, next) {
    res.render('index');                 //change from index to index.html
});

router.get('/', function(req, res, next) {
    res.render('index.html');
});

Upvotes: 0

Uili Fecteau
Uili Fecteau

Reputation: 1

I had this error, using the jsx engine though. I fixed it by making sure the file I had in my view folder was the right extension *.jsx in my case. I did have it as just index.js.

Upvotes: 0

ChevCast
ChevCast

Reputation: 59163

You're having an error and the default express error handler is trying to display the error to the user by rendering the error view. Did you use a generator to generate your initial app? If so, did you delete the error view from the views directory?

Either change the default express error handler (likely in your app.js) so that it just spits out the raw error instead of trying to render it into a nice little view, or add the error view that it's looking for.

The error handler generated by express-cli usually looks something like this:

app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: err
  });
});

Notice it's trying to render('error' and failing to find a view named "error". What makes it an error handler is the simple fact that it accepts 4 arguments, the first being the error. Express knows that if an error is caught then it should jump down to that handler.

As far as your actual error that is causing the error handler to run, I'm not sure. You'll need to fix that so that the error shows properly and then you'll be able to debug from there.

Upvotes: 3

Related Questions