apache/hadoop · error · LeaseExpiredException

INode is not a regular file: {} (inode {}) {}

Error message

INode is not a regular file: {} (inode {}) {}

What it means

checkLease found that the resolved last INode is not a regular file (a directory or symlink). A write lease only applies to files, so the caller gets LeaseExpiredException('INode is not a regular file: ...') with path, inode id, and holder context.

Source

Thrown at hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSNamesystem.java:3257

  }

  private String leaseExceptionString(String src, long fileId, String holder) {
    final Lease lease = leaseManager.getLease(holder);
    return src + " (inode " + fileId + ") " + (lease != null? lease.toString()
        : "Holder " + holder + " does not have any open files.");
  }

  INodeFile checkLease(INodesInPath iip, String holder, long fileId)
      throws LeaseExpiredException, FileNotFoundException {
    String src = iip.getPath();
    INode inode = iip.getLastINode();
    assert hasReadLock(RwLockMode.FS);
    if (inode == null) {
      throw new FileNotFoundException("File does not exist: "
          + leaseExceptionString(src, fileId, holder));
    }
    if (!inode.isFile()) {
      throw new LeaseExpiredException("INode is not a regular file: "
          + leaseExceptionString(src, fileId, holder));
    }
    final INodeFile file = inode.asFile();
    if (!file.isUnderConstruction()) {
      throw new LeaseExpiredException("File is not open for writing: "
          + leaseExceptionString(src, fileId, holder));
    }
    // No further modification is allowed on a deleted file.
    // A file is considered deleted, if it is not in the inodeMap or is marked
    // as deleted in the snapshot feature.
    if (isFileDeleted(file)) {
      throw new FileNotFoundException("File is deleted: "
          + leaseExceptionString(src, fileId, holder));
    }
    final String owner = file.getFileUnderConstructionFeature().getClientName();
    if (holder != null && !owner.equals(holder)) {
      throw new LeaseExpiredException("Client (=" + holder
          + ") is not the lease owner (=" + owner + ": "

View on GitHub (pinned to 2add963021)

Solutions

  1. Write to a file path under the directory (parent + filename), never the directory itself
  2. If the path was hijacked, pick a new output path and restart the write
  3. Type-check the target with getFileStatus().isFile() before starting long writes

Example fix

// before
FSDataOutputStream out = fs.create(dirPath);   // dirPath is a directory -> LeaseExpiredException
// after
FSDataOutputStream out = fs.create(new Path(dirPath, "part-0000"));
Defensive patterns

Strategy: validation

Validate before calling

FileStatus st = fs.getFileStatus(p);
if (!st.isFile()) throw new IllegalArgumentException("not a regular file: " + p);

Type guard

static boolean isRegularFile(FileSystem fs, Path p) throws IOException {
  try { return fs.getFileStatus(p).isFile(); }
  catch (FileNotFoundException e) { return false; }
}

Try / catch

catch (LeaseExpiredException e) { if (e.getMessage() != null && e.getMessage().contains("not a regular file")) failWithBadPathHint(); else throw e; }

Prevention

When it happens

Trigger: addBlock/close/append-family calls with a src that resolves to a directory — the writer's file was deleted and the path re-created as a directory, or the path string always was a directory path used as a file target.

Common situations: Path template bugs that append a filename to a path which itself names a directory; another job replaced the file with a same-named directory; snapshot restore changed the inode type under an active writer.

Related errors


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