Express 5 Catches Async Throws — Express 4 Didn't
Async Error Handling
Express 4 silently swallowed async errors. Express 5 forwards them to the error middleware automatically.
What you'll learn
- Know the Express 4 vs 5 difference
- Use express-async-handler on 4
- Recognize the silent-bug variant
The single biggest improvement in Express 5: async errors flow to your error middleware automatically.
Express 4 — the Trap
// Express 4
app.get("/users/:id", async (req, res) => {
const user = await db.users.findById(req.params.id);
if (!user) throw new Error("not found"); // ← silently lost
res.json(user);
}); In Express 4, that throw becomes an unhandled promise rejection. Express’s error middleware never fires. The request hangs until the client times out. Awful.
Express 4 Fix — express-async-handler
npm install express-async-handler import asyncH from "express-async-handler";
app.get("/users/:id", asyncH(async (req, res) => {
const user = await db.users.findById(req.params.id);
if (!user) throw new Error("not found");
res.json(user);
})); asyncH wraps each handler in a .catch(next). Errors flow
correctly.
Or roll your own:
const asyncH = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next); Express 5 — No Wrapper Needed
app.get("/users/:id", async (req, res) => {
const user = await db.users.findById(req.params.id);
if (!user) throw new HttpError(404, "not_found", "user not found");
res.json(user);
}); Throw, reject, return a promise that rejects — Express 5 catches all of them and routes to your error middleware.
Middleware Too
The same applies to async middleware:
async function requireAuth(req, res, next) {
req.user = await verifyToken(req.headers.authorization);
next();
} If verifyToken throws, the error flows to your error middleware.
The Subtle Bug
Don’t forget to await:
// missing await — error from db.users.create is silently lost
app.post("/users", async (req, res) => {
db.users.create(req.body); // 🚨
res.status(201).end();
}); ESLint’s @typescript-eslint/no-floating-promises rule (TS) or
promise/always-return (JS) catches these.
Why It Matters
In production, this difference is huge. Express 4 apps that don’t wrap async handlers leak request handles, hang clients, and miss errors in monitoring. Upgrade to Express 5 if you possibly can.
Middleware Order →