{"id":"94521fa442fdc3cd","repo":"expressjs/express","slug":"callback-function-required","errorCode":null,"errorMessage":"callback function required","messagePattern":"callback function required","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/application.js","lineNumber":296,"sourceCode":" * the same signature that Express expects: `(path, options, callback)`,\n * though note that it aliases this method as `ejs.__express` internally\n * so if you're using \".ejs\" extensions you don't need to do anything.\n *\n * Some template engines do not follow this convention, the\n * [Consolidate.js](https://github.com/tj/consolidate.js)\n * library was created to map all of node's popular template\n * engines to follow this convention, thus allowing them to\n * work seamlessly within Express.\n *\n * @param {String} ext\n * @param {Function} fn\n * @return {app} for chaining\n * @public\n */\n\napp.engine = function engine(ext, fn) {\n  if (typeof fn !== 'function') {\n    throw new Error('callback function required');\n  }\n\n  // get file extension\n  var extension = ext[0] !== '.'\n    ? '.' + ext\n    : ext;\n\n  // store engine\n  this.engines[extension] = fn;\n\n  return this;\n};\n\n/**\n * Proxy to `Router#param()` with one added api feature. The _name_ parameter\n * can be an array of names.\n *\n * See the Router#param() docs for more details.","sourceCodeStart":278,"sourceCodeEnd":314,"githubUrl":"https://github.com/expressjs/express/blob/a3714473feb3d2908add734d340e7755fd85e0a3/lib/application.js#L278-L314","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass the engine's render function: app.engine('html', require('ejs').renderFile).","For engines without an Express-compatible export, use an adapter such as consolidate: app.engine('hbs', require('consolidate').handlebars).","Log typeof the value you're passing and inspect the engine module's exports to find the (path, options, callback) function."],"exampleFix":"// before\napp.engine('html', require('ejs'));\n// after\napp.engine('html', require('ejs').renderFile);","handlingStrategy":"type-guard","validationCode":"if (typeof onListen !== 'function') {\n  throw new TypeError('app.listen callback must be a function');\n}\napp.listen(port, onListen);","typeGuard":"function isCallback(fn) {\n  return typeof fn === 'function';\n}","tryCatchPattern":"try {\n  app.listen(port, cb);\n} catch (err) {\n  if (/callback function required/.test(err.message)) {\n    // a non-function was passed where Express requires a callback\n  }\n  throw err;\n}","preventionTips":["Only pass a callback argument when you actually have a function; omit it entirely rather than passing null/undefined placeholders.","Beware argument-order mistakes (e.g. passing options object where the callback goes).","Use TypeScript definitions to catch wrong argument arity at compile time."],"tags":["express","template-engine","view-engine","configuration"],"analyzedSha":"a3714473feb3d2908add734d340e7755fd85e0a3","analyzedAt":"2026-07-31T18:55:36.968Z","schemaVersion":2}