alibaba/arthas · error · IOException
File '{file}' exists but is a directory
Error message
File '{file}' exists but is a directory What it means
FileUtils.openOutputStream(file, append) checks the existing target: if file.exists() && file.isDirectory(), it throws IOException("File '<file>' exists but is a directory"). You cannot open a FileOutputStream on a directory entry.
Source
Thrown at common/src/main/java/com/taobao/arthas/common/FileUtils.java:116
* The parent directory will be created if it does not exist. The file will be
* created if it 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 written to. An exception is thrown if the parent directory cannot
* be created.
*
* @param file the file to open for output, must not be {@code null}
* @param append if {@code true}, then bytes will be added to the end of the
* file rather than overwriting
* @return a new {@link FileOutputStream} for the specified file
* @throws IOException if the file object is a directory
* @throws IOException if the file cannot be written to
* @throws IOException if a parent directory needs creating but that fails
* @since 2.1
*/
public static FileOutputStream openOutputStream(final File file, final boolean append) throws IOException {
if (file.exists()) {
if (file.isDirectory()) {
throw new IOException("File '" + file + "' exists but is a directory");
}
if (!file.canWrite()) {
throw new IOException("File '" + file + "' cannot be written to");
}
} else {
final File parent = file.getParentFile();
if (parent != null) {
if (!parent.mkdirs() && !parent.isDirectory()) {
throw new IOException("Directory '" + parent + "' could not be created");
}
}
}
return new FileOutputStream(file, append);
}
/**
* Reads the contents of a file into a byte array.
* The file is always closed.View on GitHub (pinned to 21cf2e9ba5)
Solutions
- Append a filename to the path so it targets a file, not a directory.
- Validate file.isFile() (or !isDirectory()) before calling openOutputStream.
- If writing many files, loop over names inside the directory rather than writing to the directory itself.
Example fix
// before
FileUtils.openOutputStream(new File("/var/log/arthas"), false); // /var/log/arthas is a dir -> throws
// after
FileUtils.openOutputStream(new File("/var/log/arthas", "trace.log"), false); Defensive patterns
Strategy: validation
Validate before calling
if (file.exists() && file.isDirectory()) {
throw new IOException("target is a directory: " + file);
} Type guard
static boolean isWritableFile(File f) {
return f != null && (!f.exists() || f.isFile());
} Try / catch
try {
return FileUtils.openOutputStream(file, append);
} catch (IOException e) {
if (e.getMessage().endsWith("is a directory")) {
// append a filename or pick a different path
} else throw e;
} Prevention
- Always include a filename in output paths.
- Validate !file.isDirectory() before opening for output.
- Distinguish directory vs file paths in configuration schemas.
When it happens
Trigger: Passing a File path that resolves to an existing directory (e.g. output path equals a folder, or a missing filename so the path collapses to a directory).
Common situations: Output path config omitting the filename (path ends at a directory); a path collision where a log/output dir shares a name with the intended file; user supplied a directory where a file path was expected.
Related errors
- File '{file}' cannot be written to
- Directory '{parent}' could not be created
- Bad zip entry: {currentEntry}
- Failed to prepare MCP upload directory
- Failed to create MCP upload directory
AI-assisted analysis of alibaba/arthas@21cf2e9ba5 (2026-08-14).
Data as JSON: /api/errors/38af081d7ae97018.
Report an issue: GitHub.