A view name ending in "." causes Express to call require("") in the View constructor, throwing an opaque error instead of resolving the view or reporting a clean lookup error.
Reproduction:
const express = require('express')
const app = express()
app.set('view engine', 'ejs')
// callback is never invoked — throws synchronously:
app.render('index.', (err, html) => {})
// TypeError [ERR_INVALID_ARG_VALUE]: The argument 'id' must be a non-empty string. Received ''
Via res.render('index.') inside a route the same error surfaces as an opaque 500 in the error handler, instead of the usual Failed to lookup view "index." error.
Root cause: path.extname('index.') returns '.', which is truthy, so the "no extension -> use default engine" fallback in lib/view.js is skipped. this.ext stays '.', and this.ext.slice(1) becomes '', so require('') is called.
Expected: a view name ending in "." should be handled like any unresolved view — the callback should receive a normal "Failed to lookup view" error, not an opaque require("") TypeError, and app.render() must never throw past its callback.
I have a fix + regression test ready and will open a PR.
A view name ending in "." causes Express to call require("") in the View constructor, throwing an opaque error instead of resolving the view or reporting a clean lookup error.
Reproduction:
Via res.render('index.') inside a route the same error surfaces as an opaque 500 in the error handler, instead of the usual
Failed to lookup view "index."error.Root cause: path.extname('index.') returns '.', which is truthy, so the "no extension -> use default engine" fallback in lib/view.js is skipped. this.ext stays '.', and this.ext.slice(1) becomes '', so require('') is called.
Expected: a view name ending in "." should be handled like any unresolved view — the callback should receive a normal "Failed to lookup view" error, not an opaque require("") TypeError, and app.render() must never throw past its callback.
I have a fix + regression test ready and will open a PR.