The Express Router component is failing to register new routes because the handlers provided are not callable functions.

Common Causes and Fixes:

  1. Handler is an Array, Not a Function:

    • Diagnosis: Inspect the route definition. If you see router.get('/path', [handler1, handler2]) instead of router.get('/path', handler1) or router.get('/path', handler1, handler2), this is the issue. The router expects the last argument to be the handler function, or an array of middleware functions followed by the handler function. If the only argument after the path is an array of middleware, Express interprets that array as the handler, which is not a function.
    • Fix:
      • If you intend to use a single middleware function that internally calls other functions:
        router.get('/my-route', (req, res, next) => {
          // Your logic here, calling other functions as needed
          myHandlerFunction(req, res, next);
        });
        
      • If you intend to use multiple middleware functions and a final handler:
        router.get('/my-route', middleware1, middleware2, finalHandler);
        
      • If you have an array of middleware and want to add a final handler:
        const middlewareArray = [middleware1, middleware2];
        router.get('/my-route', ...middlewareArray, finalHandler);
        
    • Why it works: Express’s router.METHOD() methods expect the arguments after the path to be either middleware functions or a final handler function. An array passed directly as the handler is not a function type and thus invalid.
  2. Handler is an async Function Used Incorrectly:

    • Diagnosis: Check if your handler is an async function and if you are trying to pass it within an array as the sole handler, like router.get('/path', [asyncHandler]). While async functions are functions, the way they are sometimes structured within arrays can lead to this error if not properly handled.
    • Fix: Ensure the async function is passed directly or as the last element in a middleware array.
      const asyncHandler = async (req, res, next) => {
        try {
          // ... async operations
          res.send('Success');
        } catch (error) {
          next(error); // Always pass errors to next()
        }
      };
      
      // Correct: async function passed directly
      router.get('/async-route', asyncHandler);
      
      // Correct: async function as the final handler in a middleware chain
      router.get('/async-route-middleware', middleware1, asyncHandler);
      
    • Why it works: Express can directly execute async functions as route handlers. The error typically arises when the structure implies the async function itself is middleware within an array that’s then misidentified as the handler.
  3. Handler is a Variable Holding undefined or null:

    • Diagnosis: If you define your handler function in a separate file and import it, or if it’s conditionally defined, the variable might not be assigned a function. Check its value before passing it to router.get().
    • Fix: Ensure the imported or defined handler variable actually holds a function.
      • In the file defining the handler:
        // handler.js
        const myHandler = (req, res) => {
          res.send('Hello');
        };
        module.exports = myHandler;
        
      • In your router file:
        const myHandler = require('./handler');
        if (typeof myHandler !== 'function') {
          console.error('Error: myHandler is not a function!');
          // Handle this error appropriately, e.g., exit or throw
        }
        router.get('/path', myHandler);
        
    • Why it works: Express requires a callable function. If the variable is undefined or null, it’s not callable, and Express throws this error.
  4. Handler is a Class Instance, Not a Method:

    • Diagnosis: You might have a class with a method intended to be a handler, but you’re passing an instance of the class itself, not the method. Example:
      class MyController {
        handleRequest(req, res) {
          res.send('Handled by class method');
        }
      }
      const controllerInstance = new MyController();
      // Incorrect: passing the instance itself
      router.get('/path', controllerInstance);
      
    • Fix: Pass the specific method of the class instance.
      class MyController {
        handleRequest(req, res) {
          res.send('Handled by class method');
        }
      }
      const controllerInstance = new MyController();
      // Correct: passing the method
      router.get('/path', controllerInstance.handleRequest.bind(controllerInstance));
      // Or if 'this' context is not an issue for the method:
      // router.get('/path', controllerInstance.handleRequest);
      
      Using .bind(controllerInstance) ensures that this inside handleRequest correctly refers to controllerInstance if your method relies on it.
    • Why it works: Express expects a function. A class instance is an object, not a function. The class method is a function, and when correctly referenced and bound, Express can execute it.
  5. Typo in Handler Function Name:

    • Diagnosis: A simple typo when referencing an otherwise correctly defined handler function.
    • Fix: Double-check the spelling of the handler function name against its definition.
      // Definition
      function myActualHandler(req, res) {
        res.send('OK');
      }
      
      // Incorrect usage
      router.get('/typo-route', myAcutalHandler); // Typo: 'Acutal' instead of 'Actual'
      
      // Correct usage
      router.get('/typo-route', myActualHandler);
      
    • Why it works: If the name is misspelled, the interpreter sees an undefined variable, which evaluates to undefined, and Express receives undefined instead of a function.
  6. Conditional Logic Assigning Non-Function:

    • Diagnosis: Your handler might be assigned based on some condition, and in one branch of the condition, a non-function value (like null, undefined, or an object) is assigned to the handler variable.
    • Fix: Ensure all branches of your conditional logic assign a valid function or a default handler.
      let routeHandler;
      if (process.env.NODE_ENV === 'production') {
        routeHandler = require('./prodHandler'); // Assume prodHandler is a function
      } else {
        // Incorrect: assign null or an object instead of a function
        // routeHandler = null;
        // routeHandler = {};
        // Correct: assign a default function or throw an error
        routeHandler = (req, res) => res.send('Development handler');
      }
      router.get('/conditional-route', routeHandler);
      
    • Why it works: Similar to cause #3, if the variable routeHandler ends up holding anything other than a function, Express cannot execute it.

The next error you’ll likely encounter after fixing these is related to incorrect req and res object handling within your now-valid route handlers, or middleware ordering issues.

Want structured learning?

Take the full Nodejs course →