apache/druid · warning

Skipping deep storage directory kill: relative path is empty

Error message

Skipping deep storage directory kill: relative path is empty

What it means

constructHdfsDeletePath validates the segment's relative path before building an HDFS delete Path. If the relative path is null or empty it logs this warning and returns null, causing the kill to be skipped rather than attempting a dangerous or meaningless delete of the storage root.

Source

Thrown at extensions-core/hdfs-storage/src/main/java/org/apache/druid/storage/hdfs/HdfsDataSegmentKiller.java:183

    final FileSystem fs = dirToDelete.getFileSystem(config);
    if (!fs.exists(dirToDelete)) {
      return;
    }
    log.info("Deleting deep storage directory[%s]", dirToDelete);
    if (!fs.delete(dirToDelete, true)) {
      throw new IOException("Failed to delete deep storage directory[" + dirToDelete + "].");
    }
  }

  /**
   * Construct a path to delete from HDFS. Returns null if the path is invalid.
   * Replicates how {@link HdfsDataSegmentPusher#pushToPath} handles ':', by replacing that with '_'.
   */
  @Nullable
  private Path constructHdfsDeletePath(String relativePath)
  {
    if (Strings.isNullOrEmpty(relativePath)) {
      log.warn("Skipping deep storage directory kill: relative path is empty");
      return null;
    }
    if (relativePath.charAt(0) == '/') {
      log.warn("Skipping deep storage directory kill: relative path must not be absolute, got [%s]", relativePath);
      return null;
    }
    if (relativePath.indexOf('\\') >= 0) {
      log.warn("Skipping deep storage directory kill: backslash not allowed in path [%s]", relativePath);
      return null;
    }
    for (String segment : StringUtils.splitPreserveAllTokens(relativePath, '/')) {
      if (segment.isEmpty() || "..".equals(segment)) {
        log.warn("Skipping deep storage directory kill: invalid path[%s]", relativePath);
        return null;
      }
    }

    if (storageDirectory == null) {

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Inspect the offending segment's loadSpec in metadata store to find why the path is empty
  2. Re-push or re-ingest the affected segments so a valid relative path is stored
  3. Skip the kill for such segments; the guard is intentional protection against deleting the whole storage directory

Example fix

// before
if (Strings.isNullOrEmpty(relativePath)) { log.warn("Skipping deep storage directory kill: relative path is empty"); return null; }
// after
if (Strings.isNullOrEmpty(relativePath)) {
  log.warnEvents("Skipping deep storage directory kill: relative path is empty for segment [%s]", segment.getIdentifier());
  return null;
}
Defensive patterns

Strategy: validation

Validate before calling

if (segmentPath == null || segmentPath.isEmpty()) {
  throw new IllegalArgumentException("segment relative path must not be empty");
}

Type guard

static boolean hasNonEmptyRelativePath(DataSegment s) {
  Object p = s.getLoadSpec().get("path");
  return p instanceof String && !((String) p).isEmpty();
}

Prevention

When it happens

Trigger: A DataSegment whose loadSpec path (relative portion after the storage directory) is empty — typically a segment with a malformed or minimal loadSpec passed to dirToDelete/kill.

Common situations: Segments loaded from old Druid versions or external ingestion that wrote segments without a proper path, or custom deep-storage adapters producing segments with empty paths.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/6f40a4bbe0e41caf. Report an issue: GitHub.