apache/hadoop · error · IOException

Possible cyclic loop while following symbolic link " + origi

Error message

Possible cyclic loop while following symbolic link " + original

What it means

Thrown in Stat.parseExecResult when stat output ends with 'Too many levels of symbolic links' (ELOOP). Stat catches this specific errno text and rethrows as IOException flagging a probable cycle, because following the symlink chain (dereference mode, stat -L) hit the kernel's symlink-depth limit.

Source

Thrown at hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/Stat.java:124

          "stat is not supported on this platform");
    }
  }

  @Override
  protected void parseExecResult(BufferedReader lines) throws IOException {
    // Reset stat
    stat = null;

    String line = lines.readLine();
    if (line == null) {
      throw new IOException("Unable to stat path: " + original);
    }
    if (line.endsWith("No such file or directory") ||
        line.endsWith("Not a directory")) {
      throw new FileNotFoundException("File " + original + " does not exist");
    }
    if (line.endsWith("Too many levels of symbolic links")) {
      throw new IOException("Possible cyclic loop while following symbolic" +
          " link " + original);
    }
    // 6,symbolic link,6,1373584236,1373584236,lrwxrwxrwx,andrew,andrew,`link' -> `target'
    // OR
    // 6,symbolic link,6,1373584236,1373584236,lrwxrwxrwx,andrew,andrew,'link' -> 'target'
    StringTokenizer tokens = new StringTokenizer(line, ",");
    try {
      long length = Long.parseLong(tokens.nextToken());
      boolean isDir = tokens.nextToken().equalsIgnoreCase("directory") ? true
          : false;
      // Convert from seconds to milliseconds
      long modTime = Long.parseLong(tokens.nextToken())*1000;
      long accessTime = Long.parseLong(tokens.nextToken())*1000;
      String octalPerms = tokens.nextToken();
      // FreeBSD has extra digits beyond 4, truncate them
      if (octalPerms.length() > 4) {
        int len = octalPerms.length();
        octalPerms = octalPerms.substring(len-4, len);

View on GitHub (pinned to 2add963021)

Solutions

  1. Lstat the link instead: construct Stat with dereference=false, or use fs.getFileLinkStatus(p) which does not follow links
  2. Find and break the cycle: walk getFileLinkStatus until revisiting a target, then delete or repoint one link
  3. Guard tree walks against revisiting already-seen symlink targets

Example fix

// before
FileStatus st = new Stat(path, true, shell).getFileStatus(); // follows links, ELOOP

// after
FileStatus st = new Stat(path, false, shell).getFileStatus(); // lstat semantics
Defensive patterns

Strategy: validation

Validate before calling

// do not follow links when a cycle is possible
boolean dereference = false;
FileStatus st = new Stat(path, dereference, shell).getFileStatus();
// or link-only status: fs.getFileLinkStatus(p)

Try / catch

try {
  st = new Stat(path, true, shell).getFileStatus();
} catch (IOException e) {
  if (e.getMessage() != null && e.getMessage().contains("cyclic")) {
    // ELOOP detected: switch to lstat and break the loop
    st = new Stat(path, false, shell).getFileStatus();
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: new Stat(path, true, shell) (dereference=true) where path is a symlink that points back into its own chain: a -> b, b -> a, or link pointing to an ancestor directory containing itself.

Common situations: Build/cleanup scripts walking directory trees that contain self-referential links; test fixtures that create circular links deliberately; migration artifacts where a link and its target were swapped.

Related errors


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/d88d4f8524e147f8. Report an issue: GitHub.