Async errors surface differently: thrown errors inside callbacks need explicit handling; promise rejections need .catch; async/await uses try/catch. Uncaught errors crash or log via process events.
Patterns
- Sync — try/catch around code that throws
- Promises —
.catchor try/catch with await - Express — async route errors forwarded with
next(err)or centralized error middleware
Custom errors
class AppError extends Error {
constructor(message, status = 500) {
super(message);
this.status = status;
}
}
Important interview questions and answers
- Q: unhandledRejection vs uncaughtException?
A: Rejection is an unhandled failed Promise; exception is a sync throw outside try/catch—both should be logged; process may exit depending on policy. - Q: Should you expose stack traces to API clients?
A: No in production—log internally, return generic message and correlation id.
Self-check
- How do you catch a failed await?
- Why create custom error classes with status codes?