17 common Node.js errors and how to solve them

mangelosanto

Matt Angelosanto

Posted on November 1, 2023

17 common Node.js errors and how to solve them

Written by David Omotayo✏️

Errors can be a significant challenge for developers, often leading to unnecessary time consumption. 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 has been around for quite some time and has been instrumental in building robust and sophisticated web services that have scaled effectively and stood the test of time. But, like every other runtime environment or platform, Node.js 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.

Jump ahead:

Unhandled exceptions in streams

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);
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

To address unhandled exception errors in Node.js streams you can use one of several solutions. Let’s take a look.

Attach error event handlers

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);
Enter fullscreen mode Exit fullscreen mode

Use try-catch with synchronous code

When 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
}
Enter fullscreen mode Exit fullscreen mode

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.

Use the pipeline method

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");
    }
  }
);
Enter fullscreen mode Exit fullscreen mode

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.

Use the finished() function

The 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.');
  }
});
Enter fullscreen mode Exit fullscreen mode

JavaScript heap out of memory error

The 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
Enter fullscreen mode Exit fullscreen mode

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.

Unclosed connections

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}`);
});
Enter fullscreen mode Exit fullscreen mode

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.

Undisposed objects

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);
Enter fullscreen mode Exit fullscreen mode

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.

Circular references

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);
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

Large applications

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
Enter fullscreen mode Exit fullscreen mode

Windows:

  • * Open the Command shell, or PowerShell as an administrator
    • Navigate to your Node.js application directory using cd
    • Run the following command:
node --max-old-space-size=4096 server.js
Enter fullscreen mode Exit fullscreen mode

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

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 defined

The 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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");
}
Enter fullscreen mode Exit fullscreen mode

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 defined

The 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
}
Enter fullscreen mode Exit fullscreen mode

ReferenceError: XMLHttp Request is not defined

The 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);
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Network and communication errors

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 ECONNRESET

The 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)
Enter fullscreen mode Exit fullscreen mode

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:

  • Reduce the amount of data that your Node.js application is sending
  • Try to connect to the remote server at a later time
  • Make sure your request configuration is correct. This includes the HTTP method (e.g., GET or POST), the host name (e.g., logrocket.com), the path (e.g., /blog), and more
  • Test your network connection
  • Increase connection timeout; the server may be timing out before a connection is established

Error: connect ECONNREFUSED

The 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)
Enter fullscreen mode Exit fullscreen mode

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 use

Error: 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
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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> 
Enter fullscreen mode Exit fullscreen mode

This command will terminate the process, after which you can start your Node.js application without any errors.

Error: write EPIPE

The 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)
Enter fullscreen mode Exit fullscreen mode

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.

Additional common Node.js errors

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 function

The .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 );
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Uncaught SyntaxError: Unexpected identifier

The 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
Enter fullscreen mode Exit fullscreen mode

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.

Conclusion

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!


200’s only Monitor failed and slow network requests in production

Deploying 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 Signup

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.

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.

💖 💪 🙅 🚩
mangelosanto
Matt Angelosanto

Posted on November 1, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related