Common Node.js Bugs CodeHawk Catches (So You Don't Have To)
JavaScript and Node.js are forgiving languages. No compile step, no type checker running on your CI pipeline (unless you choose TypeScript), no static analysis by default. That forgiveness is great for velocity. It's terrible for the bugs that sneak into production.
CodeHawk specializes in catching the bugs that Node.js teams ship accidentally — often in live services, often on Friday afternoons.
Here are the five most common Node.js/JavaScript bugs CodeHawk catches in typical Express, Next.js, and Node backend codebases.
1. Missing Error Handler in async/await
The most common mistake: you await a Promise, but you don't handle the case where it rejects.
// Before CodeHawk
async function createUser(name, email) {
const user = await db.users.create({ name, email }); // could throw
return user;
}
// In your Express handler
app.post('/users', async (req, res) => {
const user = await createUser(req.body.name, req.body.email); // no try/catch
res.json(user);
});
If the database is down, if the email is invalid, if the insert fails — the Promise rejects, the request handler never sends a response, and your client hangs. No 500 error logged anywhere.
CodeHawk flags this: "Promise might reject but is not caught. Wrap in try/catch or add .catch() handler."
The fix: 1-minute job.
async function createUser(name, email) {
try {
const user = await db.users.create({ name, email });
return user;
} catch (err) {
logger.error('User creation failed:', err);
throw err; // let the caller know
}
}
Now if the database fails, your error is logged and your handler can send a 500 response. This one mistake (missing error handler) is in the top 3 causes of "mysterious hangs" in Node.js services.
2. Unresolved Promise in Request Handlers
You call an async function but forget to await it. The function runs, but the request handler moves on and responds before the function finishes.
// Before CodeHawk
app.post('/orders/:id', async (req, res) => {
const order = await db.orders.find(req.params.id);
// Send confirmation email (but forget to wait for it)
sendEmail(order.customer.email, "Order confirmed");
// This responds before sendEmail finishes
res.json({ success: true });
});
The email goes out eventually (or doesn't), but you have no control over it. If the email service is slow, it's stuck in limbo. If there's an error, you'll never know because nothing is catching it.
CodeHawk flags: "Promise is not awaited. Function call returns immediately."
The fix: Either await it or deliberately fire-and-forget with logging:
// Option 1: Wait for it
await sendEmail(order.customer.email, "Order confirmed");
res.json({ success: true });
// Option 2: Fire-and-forget (if that's intentional)
sendEmail(...)
.catch(err => logger.error('Email failed:', err));
res.json({ success: true });
3. Accessing Properties on Objects That Might Not Exist
In Express, you often access nested properties from req.body, req.query, or API responses. If the property doesn't exist, you get a TypeError.
// Before CodeHawk
app.post('/users', async (req, res) => {
const userId = req.body.user.id; // what if user is undefined?
const email = req.body.email; // what if email is missing?
// ...
});
Crashes if the client doesn't send user or if the nesting is wrong.
CodeHawk flags: "req.body.user might be undefined. Accessing .id will throw."
The fix: Add guards.
const userId = req.body.user?.id; // optional chaining
const email = req.body.email || ''; // fallback
Or validate upfront (better practice):
if (!req.body.user || !req.body.user.id) {
return res.status(400).json({ error: 'Missing user.id' });
}
4. SQL Injection in Query Strings
This is a security issue, but it's shockingly common in Node backends that use raw SQL.
// Before CodeHawk — DANGEROUS
const userId = req.query.id;
const result = await db.query(`SELECT * FROM users WHERE id = ${userId}`);
If userId is 1 OR 1=1, you just selected all users instead of one. If it's 1; DROP TABLE users;, you know the rest.
CodeHawk flags: "User-controlled input directly embedded in SQL query. Use parameterized queries."
The fix: Use parameterized queries. Every Node.js database library supports them.
const result = await db.query(
'SELECT * FROM users WHERE id = ?',
[userId] // input is escaped automatically
);
Takes 10 seconds to fix. Protects against a critical vulnerability.
5. Missing Return Statements
You're building an async function that returns early on error, but you forget the return keyword.
// Before CodeHawk
async function validateUser(user) {
if (!user.email) {
throw new Error('Email is required'); // returns undefined, then continues
}
const isBlocked = await checkBlocklist(user.email);
if (isBlocked) {
throw new Error('User is blocked'); // returns undefined, then continues
}
return true; // only reaches here if validation passed
}
Actually, this one throws, so it would error. But if you return instead of throw, you'd silently return undefined and the function would keep executing.
// Subtle bug
if (!user.email) {
return; // missing error — just returns undefined
}
// function keeps running...
CodeHawk flags: "Function has paths that return early but also paths that continue. Make sure this is intentional."
The fix: Be explicit about whether you're throwing or returning.
if (!user.email) {
throw new Error('Email is required'); // explicit fail
}
Why This Matters for Node.js Specifically
Node.js has a great library ecosystem and tooling, but it doesn't have a compiler or type system by default. That means:
- No build step to catch mistakes
- No static analysis unless you add it
- Mistakes only surface at runtime (sometimes in production)
CodeHawk runs on every PR and catches these patterns before they ship. For Node.js teams, it's the closest thing to having a compiler that checks code logic and security.
Try CodeHawk on Your Next PR
Install CodeHawk at github.com/apps/codehawk-crossgen and merge a PR that has one of these patterns. You'll see exactly how useful a second pair of eyes can be.
And if CodeHawk flags something on your code and you're not sure if it's right, drop a comment in the PR. CodeHawk will explain further.
The rest comes down to tests, review discipline, and luck.
CodeHawk covers the mechanical part. The rest is on you.