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 error

View on GitHub (pinned to 115d590110)

Solutions

  1. Check that the path points to a file, not a directory: use file.isFile() before calling openInputStream.
  2. If you intended to read all files in a directory, use file.listFiles() and iterate, calling openInputStream on each individual file.
  3. 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

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


AI-assisted analysis of alibaba/spring-cloud-alibaba@115d590110 (2026-08-14). Data as JSON: /api/errors/a730c4edef57fd4c. Report an issue: GitHub.