alibaba/spring-cloud-alibaba · error · IOException
File '{}' exists but is a directory
Error message
File '{}' exists but is a directory What it means
Thrown by FileUtils.openInputStream (a copy of Apache Commons IO) when the File object exists on the filesystem but is a directory rather than a regular file. The method performs three sequential checks: existence, directory, readability. This is the directory check — it fires after file.exists() returns true but before attempting to open the stream, preventing a misleading error from the underlying FileInputStream constructor.
Source
Thrown at spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/io/FileUtils.java:57
* error messages than simply calling <code>new FileInputStream(file)</code>.
* <p>
* At the end of the method either the stream will be successfully opened, or an
* exception will have been thrown.
* <p>
* An exception is thrown if the file does not exist. An exception is thrown if the
* file object exists but is a directory. An exception is thrown if the file exists
* but cannot be read.
* @param file the file to open for input, must not be {@code null}
* @return a new {@link java.io.FileInputStream} for the specified file
* @throws java.io.FileNotFoundException if the file does not exist
* @throws IOException if the file object is a directory
* @throws IOException if the file cannot be read
* @since 1.3
*/
public static FileInputStream openInputStream(final File file) throws IOException {
if (file.exists()) {
if (file.isDirectory()) {
throw new IOException("File '" + file + "' exists but is a directory");
}
if (!file.canRead()) {
throw new IOException("File '" + file + "' cannot be read");
}
}
else {
throw new FileNotFoundException("File '" + file + "' does not exist");
}
return new FileInputStream(file);
}
// -----------------------------------------------------------------------
/**
* Reads the contents of a file into a String. The file is always closed.
* @param file the file to read, must not be {@code null}
* @param encoding the encoding to use, {@code null} means platform default
* @return the file contents, never {@code null}
* @throws IOException in case of an I/O errorView on GitHub (pinned to 115d590110)
Solutions
- Check that the path points to a file, not a directory: use file.isFile() before calling openInputStream.
- If you intended to read all files in a directory, use file.listFiles() and iterate, calling openInputStream on each individual file.
- Correct the configuration property or path variable to point to the specific file.
Example fix
// before
FileUtils.openInputStream(new File("/data/configs")); // is a directory
// after
FileUtils.openInputStream(new File("/data/configs/application.yml")); Defensive patterns
Strategy: validation
Validate before calling
import java.io.File;
File file = new File(path);
if (file.isDirectory()) {
throw new IllegalArgumentException(
"Path '" + path + "' is a directory, expected a file");
}
FileInputStream fis = FileUtils.openInputStream(file); Type guard
import java.io.File;
boolean isReadableFile(File file) {
return file != null && file.exists() && file.isFile() && file.canRead();
}
// Usage:
if (isReadableFile(file)) {
String content = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
} Try / catch
try {
InputStream in = FileUtils.openInputStream(file);
// use stream
} catch (IOException e) {
if (e.getMessage() != null && e.getMessage().contains("is a directory")) {
log.error("Path is a directory, not a file: {}", file);
}
throw e;
} Prevention
- Always validate file.isFile() before passing to openInputStream.
- Use Spring's Resource abstraction (ResourceUtils.getFile) which can distinguish files from directories.
- Log the resolved absolute path in configuration validation to catch directory-as-file mistakes early.
When it happens
Trigger: Calling FileUtils.openInputStream(file) (or readFileToString which delegates to it) where file points to an existing directory path. Common when a configured file path resolves to a folder, or when a path like '/data/configs' is a directory containing multiple files rather than a single file.
Common situations: 1) A configuration property specifies a directory path instead of a file path (e.g., spring config location pointing to a folder). 2) A path is dynamically constructed and accidentally resolves to a parent directory. 3) A resource that was expected to be a file was created as a directory by a deployment script.
Related errors
- File '{}' cannot be read
- File '{}' does not exist
- no enough balance
- no enough stock
- ConsulDataSource server-host is empty
AI-assisted analysis of alibaba/spring-cloud-alibaba@115d590110 (2026-08-14).
Data as JSON: /api/errors/a730c4edef57fd4c.
Report an issue: GitHub.