Error Handling in Node.js
By Syncfusion®, Inc. · Last updated
Reading Node.js errors and stack traces, error.cause, system errors with code, errno, syscall and path, common error codes, Node.js ERR_ codes, operational versus programmer errors, thrown exceptions, rejected promises and error events, custom error classes, and process-level handlers: uncaughtException, unhandledRejection, the warning event and the fail-fast strategy.
Error Handling
Error = an object that says what went wrong and where.
- In Node.js, errors come from your code, from Node.js itself and from the operating system.
- A good program expects some errors, such as a missing file.
- It handles those calmly and stops cleanly on the rest.
Simple lesson: errors are messages. Read them, handle the expected ones, and stop on the strange ones.
A Real-World Example
Think of a school bus.
- A flat tyre on the way: the driver changes it and the trip goes on. That is expected.
- The brakes fail: the driver stops the bus at once and calls for help. Going on would be dangerous.
- Either way, the driver writes down what happened in the logbook.
In Node.js, a missing file is the flat tyre. A bug in your code is the failed brakes.
You learned try, catch, finally, throw and the basic error types in Error and Exception Handling in the JavaScript course. This lesson adds what Node.js gives you on top.
What You Will Learn
In this lesson, you will learn:
- How to read a stack trace and
error.cause - System errors and their
code,errno,syscallandpath - Common error codes such as
ENOENTandEADDRINUSE - Node.js
ERR_codes - Operational errors vs programmer errors
- Thrown errors, rejected promises and error events
- Custom error classes with codes and context
uncaughtException,unhandledRejectionand thewarningevent- Why a program should fail fast
Reading Errors
Error Classes and Stack Traces
Stack trace = the list of function calls that led to the error, newest first.
// main.mjs
function readMarks(name) {
return getRegister()[name].maths;
}
function getRegister() {
return { Asha: { maths: 91 } };
}
function printReport() {
console.log(readMarks("Ravi"));
}
printReport();
Output:
file:///home/asha/school/main.mjs:3
return getRegister()[name].maths;
^
TypeError: Cannot read properties of undefined (reading 'maths')
at readMarks (file:///home/asha/school/main.mjs:3:29)
at printReport (file:///home/asha/school/main.mjs:11:15)
at file:///home/asha/school/main.mjs:14:1
Node.js v22.20.0
Reading a Stack Trace Step by Step
- The top lines show the file, the line number and the exact code, with
^under the problem. - The error line gives the type and the message:
TypeError: Cannot read properties of undefined. - The first
atline is where it broke:readMarks, line 3. - The next
atlines show who called it:printReport, then the main file. - Start fixing at the first line that is your file.
Lines that mention node:internal are inside Node.js. Skip them and look for your own file names.
Here, getRegister()["Ravi"] is undefined, because Ravi is not in the register.
error.cause
cause = the original error, kept inside a new, clearer error.
Simple lesson: read the error line first, then the first at line in your own file.
Error Types
System Errors
System error = an error from the operating system, such as "file not found".
Node.js adds extra facts to these errors:
code= a short name, such asENOENT. Check this in your code.errno= the same problem as a number, such as-2.syscall= which system job failed, such asopen.path= which file it was about.
Common Codes
| Code | Means | Usual fix |
|---|---|---|
ENOENT | No such file or folder | Check the path, or create the file |
EACCES | Permission denied | Use a folder you may write to |
EEXIST | It already exists | Use { recursive: true } or another name |
EADDRINUSE | The port is already in use | Stop the other program, or pick another port |
ECONNREFUSED | Nothing is listening at that address | Start the other server first |
ECONNRESET | The other side closed the connection | Retry, and check the other server |
ETIMEDOUT | The other side did not answer in time | Retry later, or raise the timeout |
EMFILE | Too many open files | Close files, or open fewer at once |
Node.js ERR_ Codes
- Errors made by Node.js itself have codes that start with
ERR_. - Examples:
ERR_INVALID_ARG_TYPE,ERR_MODULE_NOT_FOUND,ERR_HTTP_HEADERS_SENT. - Search the code name in the Node.js docs to learn the cause.
// main.mjs
import { readFileSync } from "node:fs";
readFileSync(42.5);
Output:
node:fs:561
getValidatedPath(path),
^
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string or an instance of Buffer or URL. Received type number (42.5)
at Object.openSync (node:fs:561:5)
at readFileSync (node:fs:444:35)
at file:///home/asha/school/main.mjs:3:1 {
code: 'ERR_INVALID_ARG_TYPE'
}
Node.js v22.20.0
Operational vs Programmer Errors
Operational error = a problem from the outside world. Expect it and handle it.
- a missing file, a server that is down, bad user input
Programmer error = a bug in the code. Fix the code. Do not hide it.
- reading a property of
undefined, calling a function with the wrong type
- reading a property of
Simple lesson: check err.code for outside problems, and fix the code for bugs.
Error Delivery Mechanisms
An error reaches you in one of three ways:
- Thrown exception = from sync code. Catch it with
try/catch. - Rejected promise = from async code. Catch it with
awaitinsidetry/catch, or with.catch(). errorevent = from an emitter, such as a stream or a server. Catch it withon("error", ...).
Choosing the Right Mechanism
- Sync function that fails: throw.
- Async function that fails: reject (throwing inside an
asyncfunction does this). - Long-running object with many results: emit
error. - Never mix them in one function. Callers would not know which one to catch.
Simple lesson: try/catch for throws and awaits, and on("error") for emitters.
Four ways an error arrives, and where you catch each one How the error arrivesWhere you catch it throw new Error() a rejected promise callback(err, data) emitter.emit("error", err) try / catch await in try, or .catch() if (err) first on("error", ...)
Each way an error can arrive has its own way to catch it.
Custom Errors
A custom error class gives an error a clear name, a code and extra facts.
this.name= the class name shown in logs.code= a short id that code can check. Messages may change, codes should not.- Extra fields such as
roll= context for the person fixing the problem. else throw err= only catch what you can handle. Pass the rest up.
An empty catch {} hides bugs. Always log, handle or rethrow the error.
Simple lesson: give your errors a name, a code and the facts needed to fix them.
Process-Level Handlers
These are the last safety nets on the process object.
uncaughtException= an error that nobody caught.uncaughtExceptionMonitor= see the error just before the crash, without stopping it. Good for logging.unhandledRejection= a rejected promise that nobody caught.warning= a warning from Node.js, such as a deprecation.--unhandled-rejections=mode= a flag that changes what happens with unhandled rejections. The default isthrow: they crash the program.
Why You Should Not Continue After uncaughtException
- An uncaught error means the program is in an unknown state.
- A file may be half written. A counter may be wrong. A connection may be stuck.
- Carrying on can make wrong results, which is worse than stopping.
- Fail fast = log the error, clean up quickly, exit with a non-zero code.
- A process manager then starts a fresh, clean copy (see Production Readiness).
Animation: an uncaught error is logged, the process exits, and a process manager starts a fresh copy uncaught error log it exit(1) fresh startby the manager
Fail fast: log, exit, and let a manager start a clean copy.
Simple lesson: use process handlers to log and exit, never to ignore errors and carry on.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
An empty catch {} | Hiding errors keeps the app running | Handle it, or log and rethrow | Bugs stay hidden and data goes wrong |
Checking err.message text | Messages are stable | Check err.code | A small message change breaks your code |
Calling an async function without await or .catch | Errors still reach try/catch | Always await or catch promises | An unhandled rejection crashes the program |
Keeping the app alive in uncaughtException | The handler fixes the problem | Log, clean up, exit | The program runs in a broken state |
Throwing strings: throw "bad" | Any value is fine | Throw Error objects | No stack trace, no name, no code |
| Losing the original error when wrapping | The new message is enough | Use { cause: err } | Nobody can find the real reason |
TL;DR
- Stack trace = read the error line, then the first
atline in your file. cause= keeps the original error inside a new one.System error = has
code,errno,syscallandpath.ENOENTmissing,EACCESnot allowed,EADDRINUSEport busy
ERR_codes = errors made by Node.js itself.- Operational error = handle it. Programmer error = fix the code.
- Throw for sync, reject for async,
errorevent for emitters. - Custom error = a class with
name,codeand context. - Fail fast = after an uncaught error, log and exit.
Check Yourself
Exercises
What to Remember
- Read an error from the message line, then the first
atline in your own file. - System errors carry
code,errno,syscallandpath. Checkcode. - Handle operational errors. Fix programmer errors.
try/catchfor throws and awaits,on("error")for emitters.- Custom errors need a name, a code and context. Use
causewhen wrapping. - After an uncaught error, log and exit. Do not carry on.
Next: Process, OS and CLI Tools. You will read command-line input, environment variables and signals.