CPCODELAB
Node.js

Error Handling in Async Code

9 min read

Error Handling in Async Code

Unhandled errors in async functions can crash your Node.js process or go silently unnoticed. Robust error handling is one of the most important skills in production Node.js development.

try/catch with async/await

js
async function fetchUser(id) {
  try {
    const response = await fetch(`https://api.example.com/users/${id}`);
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    const user = await response.json();
    return user;
  } catch (err) {
    // Network errors, JSON parse errors, HTTP errors all land here
    console.error("fetchUser failed:", err.message);
    throw err; // Re-throw so callers can handle it too
  }
}

Global Unhandled Rejection Handler

js
process.on("unhandledRejection", function(reason, promise) {
  console.error("Unhandled Promise rejection:", reason);
  // In production, log to monitoring and exit cleanly
  process.exit(1);
});

process.on("uncaughtException", function(err) {
  console.error("Uncaught exception:", err);
  process.exit(1);
});

Custom Error Classes

js
class AppError extends Error {
  constructor(message, statusCode = 500) {
    super(message);
    this.name = "AppError";
    this.statusCode = statusCode;
  }
}

async function getUser(id) {
  if (!id) throw new AppError("User ID is required", 400);
  // ...
}

In Node.js 15+, unhandled Promise rejections terminate the process by default. Always add .catch() or try/catch around every await call.

Ready to test yourself?
Practice Node.js— quiz & coding exercises