SonarSource/sonarqube · error · IOException

Directory '%s' is a file

Error message

Directory '%s' is a file

What it means

FileUtils2.deleteDirectory throws this IOException when the given File exists but is a regular file rather than a directory. The guard exists so callers get a clear message instead of a confusing failure from the recursive delete walk.

Source

Thrown at server/sonar-process/src/main/java/org/sonar/process/FileUtils2.java:119

  /**
   * Deletes a directory recursively. Does not support symbolic link to directories.
   *
   * @param directory  directory to delete
   * @throws IOException in case deletion is unsuccessful
   */
  public static void deleteDirectory(File directory) throws IOException {
    requireNonNull(directory, DIRECTORY_CAN_NOT_BE_NULL);

    if (!directory.exists()) {
      return;
    }

    Path path = directory.toPath();
    if (Files.isSymbolicLink(path)) {
      throw new IOException(format("Directory '%s' is a symbolic link", directory));
    }
    if (directory.isFile()) {
      throw new IOException(format("Directory '%s' is a file", directory));
    }
    deleteDirectoryImpl(path);
  }

  /**
   * Size of file or directory, in bytes. In case of a directory,
   * the size is the sum of the sizes of all files recursively traversed.
   *
   * This implementation is recommended over commons-io
   * {@code FileUtils#sizeOf(File)} which suffers from slow usage of Java IO.
   *
   * @throws IOException if files can't be traversed or size attribute is not present
   * @see BasicFileAttributes#size()
   */
  public static long sizeOf(Path path) throws IOException {
    SizeVisitor visitor = new SizeVisitor();
    Files.walkFileTree(path, visitor);
    return visitor.size;

View on GitHub (pinned to 184c821202)

Solutions

  1. Check File.isDirectory() before calling deleteDirectory and delete the file with File.delete() instead
  2. Verify the path configured actually points to a directory
  3. Delete the file manually and retry

Example fix

// before
FileUtils2.deleteDirectory(target);
// after
if (target.isFile()) {
  target.delete();
} else {
  FileUtils2.deleteDirectory(target);
}
Defensive patterns

Strategy: validation

Validate before calling

if (!new File(path).isDirectory()) { throw new IllegalArgumentException(path + " is not a directory"); }
FileUtils2.deleteDirectory(new File(path));

Try / catch

try { FileUtils2.deleteDirectory(dir); } catch (IOException e) { if (e.getMessage().contains("is a file")) { /* handle file-vs-dir */ } else throw e; }

Prevention

When it happens

Trigger: Calling FileUtils2.deleteDirectory(new File(path)) where path points to a regular file, not a directory (and is not a symlink).

Common situations: Path built from config/env with wrong trailing component; a file replaced a directory between planning and execution; code assumes extension implies directory.

Related errors


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/3cfcfc28d0aa4b7e. Report an issue: GitHub.