Fixing errors takes up a lot of development time. We might be quick to point fingers at the programming language or environment, but it is fair to acknowledge that many of these errors stem from developer mistakes and how we use these tools.
Node.js is instrumental for building robust and sophisticated web services that scale effectively but, like every other runtime environment or platform, it is susceptible to developer mistakes.
In this article, we’ll take a look at some of the most common Node.js errors you may encounter and we’ll discuss how to fix them.
The Replay is a weekly newsletter for dev and engineering leaders.
Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.
Streams are a fundamental concept in Node.js for reading and writing to asynchronous data sources such as files, sockets, or HTTP requests. Errors can occur at any time during a stream’s lifecycle.
Streams emit errors during various operations, such as reading, writing, piping, or transforming data. The errors are emitted through the stream’s error event. If you do not attach an error handler to the stream, the errors will propagate up the event loop and potentially crash your application.
Here’s an example of an unhandled exception in streams:
const fs = require("fs");
const readStream = fs.createReadStream("nonexistent-file.txt");
// Without an error handler, this error will crash the application.
readStream.pipe(process.stdout);
Without an error handler, if the connection from the client is terminated abruptly, the readStream may not be closed when the error occurs. Instead, it will remain open indefinitely, leading to memory leaks in your application.
This will likely result in unexpected behaviors and may lead to errors such as unhandled stream error in pipe, as shown below:
stream.js:60
throw er; // Unhandled stream error in pipe.
^
Error: socket hang up
at createHangUpError (_http_client.js:200:15)
at Socket.socketOnEnd (_http_client.js:285:23)
at emitNone (events.js:72:20)
at Socket.emit (events.js:166:7)
at endReadableNT (_stream_readable.js:905:12)
at nextTickCallbackWith2Args (node.js:437:9)
at process._tickCallback (node.js:351:17)
To address unhandled exception errors in Node.js streams you can use one of several solutions. Let’s take a look.
Always attach an error event handler to catch and handle errors that occur during stream operations. This ensures that errors are caught and properly managed, preventing your app from crashing:
const fs = require('fs');
const readStream = fs.createReadStream('example-file.txt');
readStream.on('error', (err) => {
console.error('An error occurred:', err.message);
});
readStream.pipe(process.stdout);
try-catch with synchronous codeWhen working with synchronous code that interacts with streams, you can wrap the code in a try-catch to handle errors effectively. This will ensure that the program does not crash if an error occurs and that the error is handled in a controlled manner:
const fs = require('fs');
try {
const readStream = fs.createReadStream('example-file.txt', 'utf8');
const dataPromise = new Promise((resolve, reject) => {
let data = '';
readStream.on('data', (chunk) => {
data += chunk;
});
// Handle errors from the stream
readStream.on('error', (err) => {
reject(err); // Reject the promise if an error occurs
});
// When the stream ends, resolve the promise with the data
readStream.on('end', () => {
resolve(data);
});
});
const fileData = await dataPromise;
console.log('File contents:', fileData);
} catch (err) {
console.error('An error occurred:', err.message); // Log error to the console
}
In the code above, we created a try-catch block that encapsulates a promise that is resolved when the stream ends successfully or is rejected if an error occurs. The error is then caught in the catch block, where it is logged.
The previous options handle errors efficiently, but it can be unmanageable to attach event handlers to every stream when using the pipe method. Instead, the pipeline method offers a much cleaner and more manageable way to handle errors.
A pipeline is a stream method that takes three arguments: a readable stream (the source of the stream), a writable stream (the destination of the stream), and a callback function that will be called if an error occurs during the process:
const fs = require('fs');
const { pipeline } = require('stream');
const readStream = fs.createReadStream("inputexample.txt");
const writeStream = fs.createWriteStream("outputexample.txt");
pipeline(
readStream, // Readable stream
writeStream, // Writable stream
(err) => {
// Callback function to handle errors
if (err) {
console.error("Pipeline failed:", err);
} else {
console.log("Pipeline succeeded");
}
}
);
The pipeline method is particularly useful for file input/output and network operations, as it provides a cleaner and more robust way to transfer data from a readable stream to a writable stream while efficiently handling errors.
finished() functionThe finished() function is a stream method that handles cleanup and completion logic for streams. It is triggered when a stream is no longer readable or writable, or when it has experienced an error due to premature termination, such as an aborted HTTP request.
The function emits an end or finish event when a stream has successfully ended. However, the function ignores these events and instead invokes a callback function, which it takes as a second argument to handle unexpected errors and prevent the application from crashing:
const { finished } = require('node:stream');
const fs = require('node:fs');
const readStream = fs.createReadStream('input.txt');
finished(readStream, (err) => {
if (err) {
console.error('Read stream encountered an error:', err);
} else {
console.log('Read stream has finished successfully.');
}
});
JavaScript heap out of memory errorThe JavaScript heap out of memory error can be caused by a number of factors, but the most prevalent is memory leaks in your application. A memory leak occurs when an application allocates memory but does not release it when it is no longer needed.
This can happen when an application creates objects that are never deleted, or when an application holds onto references to objects that are no longer being used. Over time, memory leaks can cause an application to run out of memory and crash:
app/web.1 FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
This error is rather ambiguous, and newer developers are often unsure how to resolve such an error. Let’s take a look at some of the most common causes of memory leaks in Node.js.
When a connection to a database or other resource is not closed properly, the connection will remain open and consume memory:
// A route that simulates a long-running operation without closing the connection
app.get('/unclosed', (req, res) => {
// In a real scenario, this could be a database query, a file read, etc.
setTimeout(() => {
// This response is never sent, leaving the connection open
console.log('Request handled, but the response is never sent.');
}, 5000);
});
const port = 3000;
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
In this example, the server does not send a response to the client. This causes the connection to remain open, and over time, these open connections can consume memory and lead to performance issues.
To avoid this, your server should send a response to the client, even if it is a simple response indicating that the request was received. The server should also close the connection after sending the response.
When an object is no longer needed, it should be disposed of to free up the memory it is using. Failure to do so can lead to memory leaks:
const mysql = require('mysql2');
const pool = mysql.createPool({
host: 'localhost',
user: 'username',
password: 'password',
database: 'mydb',
connectionLimit: 10, // Limit the number of concurrent connections
});
// Simulate a query that doesn't release the connection
function querySim() {
pool.query('SELECT 1 + 1 AS result', (error, results) => {
if (error) {
console.error('Error:', error);
} else {
console.log('Result:', results[0].result);
}
});
}
// Periodically simulate queries (e.g., every 1 second)
setInterval(querySim, 1000);
In this example, the querySim function will continuously create new database connections using the connection pool without releasing them, leading to a memory leak.
To avoid memory leaks in production, always release resources when done with them. Use pool.end() for database connections to close the connection pool effectively.
When two objects refer to each other, they can create a circular reference. This can prevent either object from being garbage collected, which can lead to memory leaks:
// Repeating the process periodically to simulate a memory leak in a
//production environment
setInterval(() => {
// Create new objects with circular references
const newObj1 = {};
const newObj2 = {};
newObj1.child = newObj2;
newObj2.parent = newObj1;
}, 1000);
The above example simulates a situation in which two objects, obj1 and obj2, are created repeatedly. Each object has a property that points to the other object, creating a circular reference. This prevents garbage collection.
If this process continues over time, it can lead to memory leaks, as the objects with circular references will not be garbage collected and will continue to consume memory. This can eventually lead to the JavaScript heap out of memory error.
To prevent memory leaks, break circular references when objects are no longer needed. Set properties to null, or use other techniques that break the circular references when the objects are no longer in use.
Here’s how we could use null to break the references in the previous example:
let newObj1, newObj2;
function circularObjects() {
// If objects were previously created, break their references
if (newObj1) {
newObj1.child = null;
}
if (newObj2) {
newObj2.parent = null;
}
// Create new objects with circular references
newObj1 = {};
newObj2 = {};
newObj1.child = newObj2;
newObj2.parent = newObj1;
}
setInterval(circularObjects, 1000);
The JavaScript heap out of memory error can also occur when an application grows larger and uses more objects that take up all the available heap memory allocated by Node.js (1.5GB) by default.
To address this error, you can increase the maximum heap memory using the following commands:
Linux or macOS:
node --max-old-space-size=4096 server.js
Windows:
cdnode --max-old-space-size=4096 server.js
This command will start your Node.js application with 4GB of memory limit.
A reliable approach to avoid memory leaks in Node.js is to follow best coding practices and utilize tools like the Node.js Inspector to monitor and manage memory usage effectively.
Environment compatibility errors can arise when code written for a specific environment, such as a web browser, is ported to a different environment where the expected features or objects are not available or behave differently. Let’s take a look at a few common reference errors.
ReferenceError: document is not definedThe ReferenceError: document is not defined error is the most common environment compatibility error encountered by developers who are used to working in the web browser environment and are new to Node.js:
ReferenceError: document is not defined
at Object.<anonymous> (C:\Users\Desktop\main.js:9:18)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:501:10)
at startup (node.js:129:16)
at node.js:814:3
This error indicates that you are attempting to access the document global object, which is typically available in web browsers as part of the DOM. However, Node.js is a runtime environment and does not have a DOM by default. That’s why attempting to access document in a Node.js environment will lead to the ReferenceError: document is not defined error:
// This code tries to access the `document` object, which is not available outside a web browser
const title = document.title;
console.log(`Document title: ${title}`); // ReferenceError: document is not defined
If your task requires a DOM, you can use a library that provides a DOM for Node.js, such as Cheerio or Puppeteer. Otherwise, you can fix this error by checking the environment using the typeof operator to determine the specific environment your code is in before accessing the document object:
if (typeof document === "object") {
// Your browser-specific code here
document.querySelectorAll("p");
document.getElementById("table");
console.log("Running in a browser environment");
} else {
// Your non-browser code here
console.log("Running in a non-browser environment");
}
This code will check if the window object is available in the runtime environment. It will execute the code in the if block if it returns true; otherwise, it will execute the code in the else block.
ReferenceError: window is not definedThe ReferenceError: window is not defined error occurs when you try to access the window object, which is specific to web browsers and not available in a Node.js runtime environment. The window object is the global object in a web browser. It contains properties and methods that are used to interact with the browser and its window.
In a Node.js runtime environment, the global object is called global, and it does not contain the window object. Therefore, if you try to access the window object in a Node.js runtime environment, you will get the ReferenceError: window is not defined error.
Similar to the previous error, you can fix the ReferenceError: window is not defined error by using a conditional statement and the typeof operator to check if your code is in the appropriate environment before it is executed:
function nodeTask() {
console.log('Doing a Node-specific task');
}
function browserTask() {
console.log('Doing a Browser-specific task');
}
// Environment-specific code execution
if (typeof window === 'undefined') {
// Check if 'window' is undefined, which typically indicates a Node.js environment
nodeTask(); // Execute Node-specific code
} else {
// If 'window' is defined, assume it's a browser environment
browserTask(); // Execute Browser-specific code
}
ReferenceError: XMLHttp Request is not definedThe ReferenceError: XMLHttpRequest is not defined error occurs when you try to use the XMLHttpRequest constructor in Node.js to make HTTP requests, as shown in the following example:
try {
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText);
}
};
xhr.send();
} catch (error) {
console.error('Error:', error);
}
The XMLHttpRequest method is a browser-specific API. However, unlike the window and document, it is not a global object, but a constructor for interacting with servers. Due to the agnostic nature of its operation, it is easy to make the mistake of using the XMLHttpRequest method in a non-browser environment like Node.js.
To fix the ReferenceError: XMLHttpRequest is not defined error, use an alternative package such as node-fetch or axios, which are recent and provide more developer-friendly ways of interacting with the server. You can install these packages using the following commands:
npm install node-fetch npm install axios
Network and communication errors are a series of errors that typically occur during network communication between your Node.js application and other systems, such as databases, web servers, and other networked resources. These errors can be caused by various factors related to network connectivity, data transmission, and more. Let’s look at some common network and communication errors in Node.js and how to solve them.
Error: read ECONNRESETThe Error: read ECONNRESET error occurs when the connection to a remote server has unexpectedly closed, usually before the response is received, causing the HTTP request to fail:
Error: read ECONNRESET
at errnoException (server.js:900:11)
at TCP.onread (server.js:555:19)
This can be caused by a number of factors, such as the remote server being overloaded, your Node.js application sending too much data, or the remote server experiencing a power outage.
To resolve this error, do any of the following:
GET or POST), the host name (e.g., logrocket.com), the path (e.g., /blog), and moreError: connect ECONNREFUSEDThe Error: connect ECONNREFUSED error occurs when a connection from your Node.js application to a remote server cannot be established:
Error: connect ECONNREFUSED
at errnoException (net.js:770:11)
at Object.afterConnect [as oncomplete] (net.js:761:19)
This error can be caused by a number of factors, such as the remote server being down, the Node.js application not being able to reach the remote server, the remote server refusing the connection, or sending requests to the wrong endpoint or port.
To resolve this error, check the status of the remote server, verify that your Node.js application is running and is able to reach the remote server, and make sure that the remote server is not refusing the connection.
Error: listen EADDRINUSE: address already in useError: listen EADDRINUSE: address already in use is not so much a communication error, but an error that occurs when the port where you’re trying to start your Node.js application is already in use by another process:
Error: listen EADDRINUSE: address already in use :::3000
Emitted 'error' event on Server instance at:
at emitErrorNT (node:net:1544:8)
at process.processTicksAndRejections (node:internal/process/task_queues:84:21) {
code: 'EADDRINUSE',
errno: -98,
syscall: 'listen',
address: '::',
port: 3000
}
In the above example, the error message indicates that the port 3000 is already in use by another process. This means that the port is currently occupied and cannot be used for the current request.
To resolve this error, you can either stop the process that is using the specified port or configure your applications to run on a different port. To do the former, execute the following command in your terminal:
npx kill-port 3000
Note that this command will kill the process immediately, without any chance to save any unsaved data.
For macOS, use the following command to view information about the process that is using the port:
lsof -i :3000
You will receive a response that includes a process ID, PID ,number. Copy that number and run the following command with it:
kill -9 <PID number>
This command will terminate the process, after which you can start your Node.js application without any errors.
Error: write EPIPEThe Error: write EPIPE or “broken pipe” error occurs when your Node.js application tries to write data to a socket or a stream that has been closed or unexpectedly terminated at the other end of the connection. The error typically happens when your application continuously tries to write to the closed connection:
Error: write EPIPE
at errnoException (net.js:770:11)
at Socket._write (net.js:552:19)
at Socket.write (net.js:511:15)
To fix the Error: write EPIPE error, check whether the stream or socket you’re writing to is still active and running before attempting to write data. Use a try-catch block to catch the error and then take appropriate action, such as closing the stream or logging the error.
Here are a couple of additional Node.js errors that don’t fit into one of the categories we’ve already discussed, but that you’re likely to encounter sooner or later if you have not already.
.find is not a functionThe .find is not a function error occurs when you call the find() method on a data type that is not an array:
const data = {
x: 5,
y: 10,
z: 15,
};
data.find(el => el > 1 );
In this example, the find() method is being invoked on an object. Since the find() method only works on arrays, the following error will be thrown:
TypeError: arr.find is not a function
To resolve this error, ensure that you invoke the find() method on a valid array:
const data = [5, 10, 15]; data.find((el) => el > 1); // Returns 5
Uncaught SyntaxError: Unexpected identifierThe Uncaught SyntaxError: Unexpected identifier error indicates that there is a syntax error or misspelled keyword in your code that is preventing the interpreter from parsing your code correctly. This is especially common when declaring variables, classes, or methods and mistakenly using an uppercase letter instead of a lowercase letter for the keyword:
Let name = "same"; // L should be lowercase l Const age = 20; // C should be lowercase c
To resolve the Uncaught SyntaxError: Unexpected identifier error, navigate to the line where the error occurred and ensure that keywords are spelled correctly. Also look for missing symbols like commas, colons, brackets, or parentheses. By identifying the source of the error, you should be able to easily resolve it.
Errors are an integral part of what makes us good developers. By understanding them and why they occur, we gain a deeper understanding of the programming language or environment we are working on.
In this article, we reviewed some of the most common Node.js errors and discussed how to fix them. I hope this article has helped you to understand Node.js and JavaScript a bit better. Happy hacking!
Monitor failed and slow network requests in productionDeploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third-party services are successful, try LogRocket.
LogRocket lets you replay user sessions, eliminating guesswork around why bugs happen by showing exactly what users experienced. It captures console logs, errors, network requests, and pixel-perfect DOM recordings — compatible with all frameworks.
LogRocket's Galileo AI watches sessions for you, instantly identifying and explaining user struggles with automated monitoring of your entire product experience.
LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.

Promise.all still relevant in 2025?In 2025, async JavaScript looks very different. With tools like Promise.any, Promise.allSettled, and Array.fromAsync, many developers wonder if Promise.all is still worth it. The short answer is yes — but only if you know when and why to use it.

Discover what’s new in The Replay, LogRocket’s newsletter for dev and engineering leaders, in the October 29th issue.

Learn about the new features in the Next.js 16 release: why they matter, how they impact your workflow, and how to start using them.

Test out Meta’s AI model, Llama, on a real CRUD frontend projects, compare it with competing models, and walk through the setup process.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up now
One Reply to "17 common Node.js errors and how to solve them"
Mysql2 pool is built to reuse connections. You need to change mysql2 example because there is no leak in there