How to Fix EISDIR Error in Node.js

It’s incredibly frustrating when code that was working just fine yesterday suddenly throws errors the next day. One of the most common errors that developers face when working with Node.js, particularly when handling file operations, is the EISDIR: illegal operation on a directory, read error. I was facing this issue just today, and after some trial and error, I figured out what was going wrong. In this blog post, I’ll walk you through the error message EISDIR, explain what it means, why it occurs, and show you how to fix it and improve your code to avoid running into it again.

Error Code

Here’s the error I encountered:

[Error: EISDIR: illegal operation on a directory, read] {
errno: -4068,
code: 'EISDIR',
syscall: 'read'
}

Define a EISDIR?

EISDIR stands for Error, Is Directory. This error happens when you attempt to perform a file operation (like reading a file) on a directory instead of a file. The fs.readFile method, which expects a file path, gets a directory path instead, and that leads to the EISDIR error. It’s like trying to open a folder as if it were a document and the operating system simply won’t allow that.

Explain Error?

The error happens at this line of my code:

fs.readFile(filename, function(err, data) {

The filename variable here is supposed to point to the file I want to read. However, in this case, it seems that the path is pointing to a directory instead of a file. Since fs.readFile cannot read directories as files, it throws the EISDIR error.

Code Explanation

Now let’s take a closer look at the code. Here’s the basic setup of the HTTP server I wrote:

http = require('http');
var url = require('url');
var fs = require('fs');

http.createServer(function (req, res) {
var q = url.parse(req.url, true);
var filename = "." + q.pathname; // Get the full file path

fs.readFile(filename, function(err, data) {
if (err) {
console.log(err); // Log error
res.writeHead(404, {'Content-Type': 'text/html'});
return res.end("404 Not Found");
}
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data);
return res.end();
});
}).listen(8080);

console.log('8080');

Key Parts of the Code:

  1. HTTP Server Creation:
    • The server listens for incoming requests on port 8080.
  2. URL Parsing:
    • The url.parse(req.url, true) is used to parse the request URL, extracting the file path that the client requested.
  3. File Reading:
    • fs.readFile(filename, ...) attempts to read the file at the path specified by filename.
    • If the file is not found (or if it’s a directory), the server logs the error and sends a 404 Not Found response.
  4. Error Handling:
    • If there is an error (for example, the file is missing or it’s a directory), a 404 error is returned to the client.

Common Cause of the EISDIR Error

Incorrect Path Handling

The issue with my code was likely due to how the filename was constructed. When the URL requested something like http://localhost:8080/, Node.js tried to open the root directory (./), which led to the EISDIR error.

Missing or Wrong File Extension

If you’re trying to access a file like index.html but forget to include the file extension in the URL (e.g., requesting /index instead of /index.html), Node.js may treat it as a directory instead of a file.

Fix the Issue

Add File Extension Handling

One of the first fixes is to make sure the server always looks for .html files by appending .html to the filename if no file extension is provided in the URL.

Here’s the modified code:

var http = require('http');
var url = require('url');
var fs = require('fs');

http.createServer(function (req, res) {
var q = url.parse(req.url, true);
var filename = "." + q.pathname;

// Add .html extension if missing
if (filename == "./") {
filename = "./index.html"; // Default to index.html
} else if (!filename.includes(".")) {
filename += ".html"; // Append .html if no file extension is provided
}

fs.readFile(filename, function(err, data) {
if (err) {
console.log(err);
res.writeHead(404, {'Content-Type': 'text/html'});
return res.end("404 Not Found");
}
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data);
return res.end();
});
}).listen(8080);

console.log('8080');

With this change, even if someone visits http://localhost:8080/ (without specifying a file), the server will automatically serve index.html by default.

Improve Directory and File Path Validation

Another improvement is to ensure that the requested path is not a directory. Before attempting to read the file, we can use fs.stat() to check whether the path is a directory or file. If it’s a directory, we’ll return a 400 Bad Request error.

Here’s the updated code:

.stat(filename, function(err, stats) {
if (err) {
console.log(err);
res.writeHead(404, {'Content-Type': 'text/html'});
return res.end("404 Not Found");
}

if (stats.isDirectory()) {
res.writeHead(400, {'Content-Type': 'text/html'});
return res.end("400 Bad Request: Requested resource is a directory");
}

fs.readFile(filename, function(err, data) {
if (err) {
console.log(err);
res.writeHead(404, {'Content-Type': 'text/html'});
return res.end("404 Not Found");
}
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data);
return res.end();
});
});

This additional check ensures that if someone tries to access a directory instead of a file, they’ll get a clear message.

Practice Functionality

To make the server more robust, I also decided to add the following features:

Support for Other File Types

The server can handle different file types such as .css, .js, and .json. By checking the file extension, I can serve the correct content type:

 (filename.endsWith('.css')) {
res.writeHead(200, {'Content-Type': 'text/css'});
} else if (filename.endsWith('.js')) {
res.writeHead(200, {'Content-Type': 'application/javascript'});
} else if (filename.endsWith('.json')) {
res.writeHead(200, {'Content-Type': 'application/json'});
}

Static File Serving

If you have a public folder with static assets like images, CSS, or JavaScript, you can serve them directly by checking if the filename starts with ./public.

if (filename.startsWith('./public')) {
fs.readFile(filename, function(err, data) {
if (err) {
res.writeHead(404, {'Content-Type': 'text/html'});
return res.end("404 Not Found");
}
res.writeHead(200);
res.write(data);
return res.end();
});
}

This allows you to separate static files from dynamic content, keeping things organized.

Conclusion

The EISDIR error typically occurs when you try to read a directory as if it were a file. By improving how we handle file paths, adding file extensions, and checking if the requested resource is a file or directory, we can prevent this error from occurring. Implementing these changes makes your server more reliable and helps it serve content smoothly, regardless of whether it’s a file or directory. Now, with a better understanding of how to handle paths and extensions, I hope you’re better equipped to avoid this error in the future.

Related blog posts