expressjs/express · error · Error

callback function required

Error message

callback function required

What it means

app.engine(ext, fn) registers a template-engine callback for a file extension. At lib/application.js:294-297 it throws 'callback function required' when fn is not a function, since Express will later invoke it as fn(path, options, callback) during rendering and anything else cannot work.

Source

Thrown at lib/application.js:296

 * the same signature that Express expects: `(path, options, callback)`,
 * though note that it aliases this method as `ejs.__express` internally
 * so if you're using ".ejs" extensions you don't need to do anything.
 *
 * Some template engines do not follow this convention, the
 * [Consolidate.js](https://github.com/tj/consolidate.js)
 * library was created to map all of node's popular template
 * engines to follow this convention, thus allowing them to
 * work seamlessly within Express.
 *
 * @param {String} ext
 * @param {Function} fn
 * @return {app} for chaining
 * @public
 */

app.engine = function engine(ext, fn) {
  if (typeof fn !== 'function') {
    throw new Error('callback function required');
  }

  // get file extension
  var extension = ext[0] !== '.'
    ? '.' + ext
    : ext;

  // store engine
  this.engines[extension] = fn;

  return this;
};

/**
 * Proxy to `Router#param()` with one added api feature. The _name_ parameter
 * can be an array of names.
 *
 * See the Router#param() docs for more details.

View on GitHub ↗ (pinned to a3714473fe)

Solutions

  1. Pass the engine's render function: app.engine('html', require('ejs').renderFile).
  2. For engines without an Express-compatible export, use an adapter such as consolidate: app.engine('hbs', require('consolidate').handlebars).
  3. Log typeof the value you're passing and inspect the engine module's exports to find the (path, options, callback) function.

Example fix

// before
app.engine('html', require('ejs'));
// after
app.engine('html', require('ejs').renderFile);
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof onListen !== 'function') {
  throw new TypeError('app.listen callback must be a function');
}
app.listen(port, onListen);

Type guard

function isCallback(fn) {
  return typeof fn === 'function';
}

Try / catch

try {
  app.listen(port, cb);
} catch (err) {
  if (/callback function required/.test(err.message)) {
    // a non-function was passed where Express requires a callback
  }
  throw err;
}

Prevention

When it happens

Trigger: app.engine('html', undefined); passing the engine module itself rather than its render function, e.g. app.engine('html', require('ejs')) instead of require('ejs').renderFile; passing a string or options object as the second argument.

Common situations: Engines that don't expose __express and where the developer passes the wrong export (ejs.renderFile vs ejs, handlebars vs hbs.__express); consolidate.js usage errors (cons.swig vs cons.swig — must be the function property); a version upgrade of the engine package renaming its render export.

Related errors


AI-assisted analysis of expressjs/express@a3714473fe (2026-07-31). Data as JSON: /data/errors/94521fa442fdc3cd.json. Report an issue: GitHub ↗.