SonarSource/sonarqube · error · IOException

Directory ' ' is a symbolic link

Error message

Directory '%s' is a symbolic link

What it means

FileUtils2.deleteDirectory refuses to delete anything that is a symbolic link: before recursing it checks Files.isSymbolicLink and throws an IOException formatted with the directory path. This is a safety guard so a symlink pointing elsewhere (e.g. /) is never followed and recursively deleted.

Solutions

  1. Delete the symlink itself if intended (Files.delete / rm) rather than expecting recursive deletion
  2. Point the configuration at the real directory path instead of a symlinked path
  3. If symlink indirection is required, restructure so the parent directory is real and only contents are linked
  4. Handle/expect the IOException in code that cleans directories and decide explicitly how to treat symlinks

Example fix

// before
rm -rf /opt/sonarqube/temp   # temp -> /mnt/bigdisk/temp (symlink)
// after
rm /opt/sonarqube/temp        # remove link itself
rm -rf /mnt/bigdisk/temp/     # clean the real target explicitly
Defensive patterns

Strategy: try-catch

Validate before calling

if (Files.isSymbolicLink(directory.toPath()))
  throw new IllegalArgumentException("Refusing to delete symlinked dir: " + directory);

Try / catch

try { FileUtils2.deleteDirectory(dir); } catch (IOException e) {
  if (e.getMessage() != null && e.getMessage().contains("symbolic link")) {
    Files.delete(dir.toPath()); // remove the link itself
  } else { throw e; }
}

Prevention

When it happens

Trigger: deleteDirectory (or deleteQuietly) is called with a File that resolves to a symlink, e.g. a temp dir path that is a symlink, or a project/web dir replaced by a symlink during deployment.

Common situations: Deployments where /opt/sonarqube/data or temp dirs are symlinked to another volume; users symlinking logs/temp for space management; containers mounting volumes through symlinks.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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

Appendix: source

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

    }
  }

  /**
   * 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 {

View on GitHub (pinned to 184c821202)