apache/hadoop · error · FileNotFoundException

Item not found: %s

Error message

Item not found: %s

What it means

GoogleCloudStorageFileSystem.delete throws FileNotFoundException("Item not found: <path>") when getFileInfo(path).exists() is false — deleting something that is not there. Deleting the gs:// root is rejected earlier by checkArgument. The exists-check and the actual delete are separate calls, so a path can also vanish between stat and delete.

Source

Thrown at hadoop-cloud-storage-project/hadoop-gcp/src/main/java/org/apache/hadoop/fs/gs/GoogleCloudStorageFileSystem.java:242

    }

    // Create only a leaf directory because subdirectories will be inferred
    // if leaf directory exists
    try {
      gcs.createEmptyObject(resourceId);
    } catch (FileAlreadyExistsException e) {
      // This means that directory object already exist, and we do not need to do anything.
      LOG.trace("mkdirs: {} already exists, ignoring creation failure", resourceId, e);
    }
  }

  void delete(URI path, boolean recursive) throws IOException {
    checkNotNull(path, "path should not be null");
    checkArgument(!path.equals(GCSROOT), "Cannot delete root path (%s)", path);

    FileInfo fileInfo = getFileInfo(path);
    if (!fileInfo.exists()) {
      throw new FileNotFoundException("Item not found: " + path);
    }

    List<FileInfo> itemsToDelete;
    // Delete sub-items if it is a directory.
    if (fileInfo.isDirectory()) {
      itemsToDelete =
          recursive
              ? listRecursive(fileInfo.getPath()) // TODO: Get only one result
              : listDirectory(fileInfo.getPath());

      if (!itemsToDelete.isEmpty() && !recursive) {
        throw new DirectoryNotEmptyException("Cannot delete a non-empty directory. : " + path);
      }
    } else {
      itemsToDelete = new ArrayList<>();
    }

    List<FileInfo> bucketsToDelete = new ArrayList<>();

View on GitHub (pinned to 2add963021)

Solutions

  1. Treat FileNotFoundException from delete as success where delete-idempotency is the goal.
  2. Pre-check with getFileInfo(path).exists() for clearer control flow (still combine with catching, due to the race window).
  3. Fix ordering/ownership in cleanup logic so only one writer deletes a given path.

Example fix

// before
fs.delete(path, true); // throws if already gone

// after
try {
  fs.delete(path, true);
} catch (FileNotFoundException e) {
  // already deleted by someone else: idempotent success
}
Defensive patterns

Strategy: validation

Validate before calling

if (gcsFs.getFileInfo(path).exists()) {
  gcsFs.delete(path, recursive);
} else {
  LOG.debug("delete skipped, already gone: {}", path);
}

Try / catch

catch (FileNotFoundException e) {
  // someone else removed it first: treat as success for idempotent cleanup
}

Prevention

When it happens

Trigger: delete(path, recursive) for a nonexistent object, bucket, or directory; concurrent deletion by another client between your listing and your delete; retrying a job whose earlier attempt already deleted the path.

Common situations: Concurrent cleanup jobs racing each other; committer cleanup removing temp files a retried task also tries to delete; typo'd paths; idempotent job retries.

Related errors


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