Fixing 'Route.get() requires a callback function but got [object Undefined]' in Express.js

beginner๐Ÿ’š Node.js2026-05-20| Node.js 14+, Express.js 4.x, any OS (Linux/macOS/Windows)

Error Message

Error: Route.get() requires a callback function but got a [object Undefined]
#express#routing#middleware#undefined#nodejs

The Error

Error: Route.get() requires a callback function but got a [object Undefined]

Your Express server crashes at startup โ€” before it handles a single request. The stack trace points straight to the offending router.get(), app.get(), or router.post() call.

Why This Happens

Express validates every argument you pass to a route. Pass anything that isn't a function โ€” including undefined โ€” and it throws immediately. No recovery, no fallback.

The root cause is almost always one of two things:

  • A variable that resolved to undefined because of an import or require problem
  • A function that doesn't exist at the path you're importing from (named export mismatch)

Most Common Causes and Fixes

1. Named export mismatch (most frequent)

You export a function under one name but import it under a different one. The result is silently undefined โ€” no warning, just a crash at startup.

// controllers/userController.js
const getUsers = (req, res) => res.json([]);
module.exports = { getUsers };

// routes/users.js โ€” WRONG
const { getAllUsers } = require('../controllers/userController'); // undefined!
router.get('/', getAllUsers); // ๐Ÿ’ฅ crash

// routes/users.js โ€” CORRECT
const { getUsers } = require('../controllers/userController');
router.get('/', getUsers);

Before registering the route, log what you actually imported:

const { getUsers } = require('../controllers/userController');
console.log(typeof getUsers); // 'function' = good, 'undefined' = problem
router.get('/', getUsers);

2. Circular dependency between files

A circular dependency is sneaky: File A requires File B, which requires File A back. Node.js breaks the cycle by returning an incomplete module object for the first file โ€” so your imports come out as undefined.

// app.js requires routes/users.js
// routes/users.js requires app.js  โ† circular!

// Fix: extract shared logic to a neutral file (e.g., db.js or services/user.js)
// Both files import from there โ€” no cycle.

3. Middleware not destructured from its export

If your middleware file uses named exports, you need to destructure on import. Skip that and you get the whole module object โ€” not the function itself.

// middleware/auth.js
module.exports = { authMiddleware };

// WRONG โ€” grabs the module object, not the function
const authMiddleware = require('./middleware/auth');
router.get('/profile', authMiddleware, getProfile); // ๐Ÿ’ฅ object, not a function

// CORRECT
const { authMiddleware } = require('./middleware/auth');

4. Default export vs named export confusion (ES Modules)

// controllers/postController.js
export const getPosts = (req, res) => res.json([]);
// No default export!

// routes/posts.js โ€” WRONG
import getPosts from '../controllers/postController'; // undefined (no default)
router.get('/', getPosts); // ๐Ÿ’ฅ

// CORRECT
import { getPosts } from '../controllers/postController';

5. Middleware factory missing a return statement

Write a factory that builds middleware, forget the return, and Express gets undefined instead of a function.

// WRONG โ€” returns undefined
function rateLimiter(limit) {
  const fn = (req, res, next) => { next(); };
  // forgot to return fn!
}
router.get('/', rateLimiter(10), getUsers); // ๐Ÿ’ฅ

// CORRECT
function rateLimiter(limit) {
  return (req, res, next) => { next(); };
}
router.get('/', rateLimiter(10), getUsers);

6. Route file loaded before async initialization finishes

If a controller depends on a DB connection or config that loads asynchronously, requiring it too early hands you an empty or partially initialized module.

// Move initialization before route registration
await connectDB(); // wait for this first
const userRoutes = require('./routes/users'); // then load routes
app.use('/users', userRoutes);

Step-by-Step Debugging

  • Read the stack trace. It tells you exactly which router.get() or app.post() call failed.
  • Go to that line. Log every argument before passing it:

console.log({ myHandler, myMiddleware }); // spot the undefined router.get('/path', myMiddleware, myHandler);

  
  - Find the `undefined` one. Trace it back to where it was imported or defined.
  - Fix the export name, file path, or missing `return` statement.

## Verify the Fix
Restart the server and check the output:

node app.js

Should see: Server running on port 3000

No more: Error: Route.get() requires a callback function


Hit the affected route directly:

curl http://localhost:3000/users

Expect a valid response โ€” not a crash


## Tips to Avoid This

  - **Use TypeScript** โ€” catches export/import mismatches at compile time, before the server ever starts.
  - **Stay consistent with exports** โ€” pick either named exports (`module.exports = { fn }`) or a default export and stick to one style per project. Mixing both is a reliable source of this bug.
  - **Ban circular imports in CI** โ€” the `import/no-cycle` rule from `eslint-plugin-import` catches these automatically.
  - **Always return from middleware factories** โ€” if a function takes arguments and produces middleware, it must end with a `return` statement.

Related Error Notes