Node.js: unhandled promise rejection crashes my server

asked a month ago377 viewsen

5

Since Node 15+, an unhandled rejection terminates the process. How should I structure a large Express app to catch all async errors in route handlers?

🤖 AI diagnosis

AI-generated. Not an answer — the community below confirms or corrects it. Always verify before relying on it.

  • Thanks, this helped. Did you also try clearing the cache?

    marielouis 5 days ago

2 Answers

2

Wrap handlers so rejections reach your error middleware:

const wrap = fn => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
app.get('/x', wrap(async (req, res) => { /* ... */ }));

Or upgrade to Express 5, which does this automatically.

Sign in to tell others whether this worked.

answered a month ago
0

Answer

The core recommendation from the discussion is to stop relying on global unhandledRejection handlers as your main safety net and instead catch async errors at the route level, since Node 15+ will crash the process on an unhandled rejection.

For a large Express app, the common pattern is:

  1. Wrap every async route handler so rejected promises are passed to next() instead of being left unhandled:
const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};
 
app.get('/route', asyncHandler(async (req, res) => {
  const data = await someAsyncCall();
  res.json(data);
}));
  1. Centralize this so you don't have to remember to wrap each handler manually — either apply the wrapper in your route registration helper, or use a library like express-async-errors (which monkey-patches Express to auto-catch async errors) so your existing async route handlers work as-is with your normal Express error-handling middleware.

  2. Keep a standard Express error-handling middleware (the 4-arg (err, req, res, next) one) at the end of your middleware stack to actually respond to these forwarded errors instead of letting them crash the app.

  3. It's still a good idea to keep a top-level process.on('unhandledRejection', ...) / process.on('uncaughtException', ...) as a last-resort logger for anything that slips through (e.g., errors in non-request code like background jobs, timers, etc.), but it shouldn't be your primary strategy for route handlers.

Unfortunately, the rest of the discussion (the "clearing the cache" comment) doesn't seem related to this issue — that seems to be a mismatched or unrelated reply, so it's probably safe to disregard it here.

Not yet covered / worth digging into further: if you're on Express 5 (still in beta/rc at time of writing), async error handling for route handlers is built in natively, so you may not need express-async-errors at all — worth checking your Express version before adding the dependency.

Sign in to tell others whether this worked.

answered a day ago

Sign in and verify your email to post an answer.